2017暑假第二阶段第七场 总结
来源:互联网 发布:windows无法连接到cmcc 编辑:程序博客网 时间:2024/06/08 07:51
T1 最大子段和
问题描述
给出一个首尾相连的循环序列,从中找出连续的一段,使得该段中的数和最大
输入格式
第一行一个整数 n,表示有 n 个数。( 1<=n<=100000)
第二行有 n 个整数,每个数的绝对值不超过 100000.
输出格式
一个整数,表示所求结果
样例输入
4
2 -4 1 4
样例输出
7
正解是用的单调队列优化DP。
由于是个环,首先把数组复制成两倍,预处理出前缀和sum。显然,当以i结尾的连续最大和
那么维护一个单调递增的单调队列即可。
#include<stdio.h>#include<deque>#define ll long long#define Max(x,y) ((x>y)?(x):(y))using namespace std;int N;ll sum[200005],a[200005],Ans=-(1LL<<60);int main(){ int i; ll x; deque<int>Q; scanf("%d",&N); for(i=1;i<=N;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i]; for(i=1;i<=N;i++)sum[i+N]=sum[i+N-1]+a[i]; Q.push_back(0); for(i=1;i<=N*2;i++) { if(Q.size()&&(i-Q.front()>N))Q.pop_front(); if(Q.size())Ans=Max(Ans,(sum[i]-sum[Q.front()])); while(Q.size()&&sum[i]<=sum[Q.back()])Q.pop_back(); Q.push_back(i); } printf("%lld",Ans);}
当然也可以用优先队列或线段树维护,甚至可以直接搞成一道线段树求区间连续最大和且不带修改的题,但是都不如单调队列优秀。
下面是优先队列的做法:
#include<stdio.h>#include<queue>#define ll long long#define Max(x,y) ((x>y)?(x):(y))using namespace std;priority_queue<pair<ll,int> >Q;int N;ll sum[200005],a[200005],Ans=-(1LL<<60),tmp;int main(){ int i; scanf("%d",&N); for(i=1;i<=N;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i]; for(i=1;i<=N;i++)sum[i+N]=sum[i+N-1]+a[i]; Q.push(make_pair(0,0)); for(i=1;i<=N*2;i++) { while(Q.size()&&i-(Q.top().second)>N)Q.pop(); if(Q.size())Ans=Max(Ans,(sum[i]+Q.top().first)); Q.push(make_pair(-sum[i],i));//小根堆 } printf("%lld",Ans);}
T2 统计
问题描述
现在有一个数组 a,数组中有 n 个元素。定义一个函数 f(l,r)表示 i(l<=i<=r)的 个数,其中 i 符合条件:不存在 j (l<=j<=r 且 j≠i)满足 ai mod aj = 0 求
∑ni=1∑nj=1f(i,j)mod(109+7) 即所有区间中包含的符合条件的 i 的个数。
输入格式
第一行一个整数 n(n<=100000)
第二行有 n 个数,表示数组中的元素 ai,0 < ai <= 10000
输出格式
表示所求的结果。注意要取模。
很明显,对于任意一个数x,若它的左边存在一个数y,使得y|x,那么左端点在y的左边且含有x的区间中,x都不符合条件;右边同理。所以找出离每个数左右分别最近的因数位置,那么区间端点在这两数之间(不含)的区间就是这个数符合条件的区间。
可以预处理出1~10000的每个数的因数,也可以直接分解。时间复杂度O(n
找出离这个数最近的因数的位置,可以在遍历时同时处理。
#include<stdio.h>#include<cstring>#define Max(x,y) ((x>y)?(x):(y))#define Min(x,y) ((x<y)?(x):(y))#define MAXN 100005#define ll long longconst ll mod=1e9+7;int N,A[MAXN],L[MAXN],R[MAXN],lastl[10005],lastr[10005];ll Ans;// lastl[i]和lastr[i]分别记录数i上一次出现的位置int main(){ int i,j; scanf("%d",&N); for(i=1;i<=N;i++)scanf("%d",&A[i]); memset(lastr,60,sizeof(lastr)); for(i=1;i<=N;i++) { for(j=1;j*j<=A[i];j++) { if(A[i]%j!=0)continue; L[i]=Max(L[i],lastl[j]); L[i]=Max(L[i],lastl[A[i]/j]); } lastl[A[i]]=i; } for(i=1;i<=N;i++)R[i]=N+1; for(i=N;i;i--) { for(j=1;j*j<=A[i];j++) { if(A[i]%j!=0)continue; R[i]=Min(R[i],lastr[j]); R[i]=Min(R[i],lastr[A[i]/j]); } lastr[A[i]]=i; } for(i=1;i<=N;i++)Ans=(Ans+ZVCXFGbn (i-L[i])*(R[i]-i)%mod)%mod; printf("%lld",Ans);}
T3 最小差值生成树
问题描述
给定一个无向图,求它的一棵生成树,使得生成树中的最大边权与最小边权 的差最小,输出其最小差值。
输入格式
第一行两个整数 n(2接下来 m 行,第 i+1 行包含三个整数 Xi(0保证图是连通的,两个点之间最多只有一条边。
输出格式
包含一行,表示最小差值生成树的最大边与最小边的差值。
样例输入
3 3
1 2 10
1 3 20
2 3 30
样例输出
10
根据最小生成树的性质,当最短边的长度唯一确定时,最长边的长度也是唯一确定的。而且如果去掉当前的最短边再做一次最小生成树时,最长边的长度也会增长。而如果不做最小生成树,最长边的长度还会增大。因此可能的答案必须是一些最小生成树的最大边与最小边权值之差。
因此先把边按照权值从小到大排序后,枚举最小边的长度用Kruscal算出最小生成树,同时更新最优解即可。
可以优化之处在于,如果从小到大枚举最小边长度,若某一刻不能做出最小生成树,那么之后也一定不会做出最小生成树,也就是说这时就可以终止循环并输出答案了。
#include<stdio.h>#include<algorithm>#define Min(x,y) ((x<y)?(x):(y))#define MAXN 205#define MAXM 5005using namespace std;int N,M,Ans=1e9;inline int _R(){ char s=getchar();int v=0,sign=0; while((s!='-')&&(s>57||s<48))s=getchar(); if(s=='-')sign=1,s=getchar(); for(;s>47&&s<58;s=getchar())v=v*10+s-48; if(sign)v=-v; return v;}struct node{int a,b,len;}edge[MAXM];bool operator<(node x,node y){return x.len<y.len;}int fa[MAXN];int gf(int x){ if(fa[x]!=x)fa[x]=gf(fa[x]); return fa[x];}bool Kruscal(int l){ int i,cnt=0,x,y,fx,fy; for(i=l;i<=M&&cnt<N-1;i++) { x=edge[i].a;y=edge[i].b; fx=gf(x);fy=gf(y); if(fx==fy)continue; fa[fx]=fy; cnt++; } if(cnt!=N-1)return false; Ans=Min(Ans,(edge[i-1].len-edge[l].len)); return true;}int main(){ int i,j; N=_R();M=_R(); for(i=1;i<=M;i++)edge[i].a=_R(),edge[i].b=_R(),edge[i].len=_R(); sort(edge+1,edge+M+1); for(i=1;M-i+1>=N-1;i++) { for(j=1;j<=N;j++)fa[j]=j; if(!Kruscal(i))break;//上面所说的优化之处 } printf("%d",Ans);}
总结
T1如果想到了单调队列,那么是一道比较简单的DP;T2想到了上文所说的性质之后,如果有难点,只在lastl,lastr数组上;T3是最小生成树例题。
这次比赛打得不好,主要是做T1时思想江化,一直想用不是环状的O(n)做法进行改进,根本就没考虑用前缀和实现的DP,所以也没有想到单调队列,想用线段树写连续区间最大和也写炸了,导致没时间做T2,只过了T3这道例题。虽然联想到之前做过的题目是一个好习惯,但是不能拘泥于之前的方法。之前的方法难以实现,就大胆选择其他方法。同时注意时间安排,不能在一道题上耗得太久。
- 2017暑假第二阶段第七场 总结
- 2017暑假第二阶段第一场 总结
- 2017暑假第二阶段第二场 总结
- 2017暑假第二阶段第三场 总结
- 2017暑假第二阶段第四场 总结
- 2017暑假第二阶段第五场 总结
- 2017暑假第二阶段第六场 总结
- 2017暑假第二阶段第八场 总结
- 2017暑假第二阶段第九场 总结
- 第七场训练赛总结
- 大暑假集训 第二阶段
- linux兴趣小组暑假第七次讲座总结
- 2015多校第七场总结
- 2010-zzuli暑假集训选拔赛四场总结
- 2017暑假集训总结
- 2017暑假集训总结
- 2017暑假总结
- 2017暑假集训总结
- 数据库三范式理解
- AndroidPdfViewer
- one-hot转为distributed representation表示
- HBase RPC理解之一
- Java 正则表达式
- 2017暑假第二阶段第七场 总结
- Java数组
- mysql连接问题1045 Access denied for user 'root'@'localhost' (using password:YES)
- 4.17 leetcode -17 longest-substring-without-repeating-characters
- ubuntu16.04mailcatcher
- FBO
- 如何选择最好的关键词,让客户容易搜索到?
- 技术文章 | Laravel/Lumen搭建服务器性能测试
- [贪心+深搜]马的回路