个人训练记录-赛码"bestcoder"杯中国大学生程序设计冠军赛
A.Movie
题意是给n个线段,要求求出是否存在三个不相交的线段,是的话输出yes,否则输出no。根据贪心的想法,可以先找出右端点r'最小的线段,他是三条线段中最左的那条,再找出左端点l'最大的线段,他是三条线段中最右的那条,这样我们只需要找出是否存在一条线段可以放在中间,即区间[l,r],l>r',r<l'。
代码
1 #include<cstdio> 2 int n; 3 long long L,R,l,r,a,b,c,d,mi,mx,flag,u,v,i; 4 int main() 5 { 6 int test; 7 long long P=4294967296; 8 scanf("%d",&test); 9 while(test--) 10 {flag=0; 11 scanf("%d%I64d%I64d%I64d%I64d%I64d%I64d",&n,&l,&r,&a,&b,&c,&d); 12 L=l;R=r; 13 u=L;v=R; 14 mi=P;mx=0; 15 if (u>v) u^=v^=u^=v; 16 if (v<mi) mi=v; 17 if (u>mx) mx=u; 18 for (i=2;i<=n;i++) 19 { 20 L=(L*a+b)%P; 21 R=(R*c+d)%P; 22 u=L;v=R; 23 if (u>v) u^=v^=u^=v; 24 if (v<mi) mi=v; 25 if (u>mx) mx=u; 26 } 27 L=l;R=r; 28 u=l;v=r;if (u>v) u^=v^=u^=v; 29 if ((u>mi)&&(v<mx)) flag=1; 30 for (i=2;i<=n;i++) 31 { 32 if (flag) break; 33 L=(L*a+b)%P; 34 R=(R*c+d)%P; 35 u=L;v=R;if (u>v) u^=v^=u^=v; 36 if ((u>mi)&&(v<mx)) flag=1; 37 } 38 if (flag) printf("YES\n");else printf("NO\n");} 39 }
B.Cycle
题意是问给一张无向图,是否存在一个奇环或者偶环,环可以经过同样的点,做法是需要先求出dfs求出一颗树,一些边为树边,一些边为非树边,首先一条非树边和一些树边可以构成一些环,我们可以先判断这些环长度的奇偶性,然后如果一个环可能由多条非树边构成,需要怎么求,也很简单,如果存多个由一条非树边构成的环,他们交集非空,那么有他们复合成环的长度的奇偶性即为这些环的奇偶性的和,即使这些环的交集可能经过一些边,也不要紧,因为构成的环的边是否存在,是根据有多少个环包含这条边所决定,若存在奇数个环包含该边,则该边存在,否则不存在,因而复合环的奇偶性等价于这些环的奇偶性的和。
代码
1 #pragma comment(linker, "/STACK:102400000,102400000") 2 #include<cstdio> 3 const int M = 601010; 4 const int N = 201000; 5 int vis[N],deep[N],jump[N][19],i,a[N],b[N],v[N][2]; 6 int dp,p[N],pre[M],tt[M],id[M],flag[N],n,m,Flag[2],s[N][2]; 7 void link(int x,int y,int z) 8 { 9 dp++;pre[dp]=p[x];p[x]=dp;tt[dp]=y;id[dp]=z; 10 } 11 void dfs(int x,int fa) 12 { 13 vis[x]=1; 14 deep[x]=deep[fa]+1; 15 jump[x][0]=fa; 16 int i; 17 for (i=1;i<=18;i++) 18 jump[x][i]=jump[jump[x][i-1]][i-1]; 19 i=p[x]; 20 while (i) 21 { 22 if (!vis[tt[i]]) 23 { 24 dfs(tt[i],x); 25 flag[id[i]]=1; 26 } 27 i=pre[i]; 28 } 29 } 30 int lca(int a,int b) 31 { 32 if (deep[a]<deep[b]) a^=b^=a^=b; 33 int i; 34 for (i=18;i>=0;i--) 35 if (deep[jump[a][i]]>=deep[b]) a=jump[a][i]; 36 if (a==b) return a; 37 for (i=18;i>=0;i--) 38 if (jump[a][i]!=jump[b][i]) 39 { 40 a=jump[a][i];b=jump[b][i]; 41 } 42 return jump[a][0]; 43 } 44 void gao(int x) 45 { 46 int i; 47 s[x][0]+=v[x][0]; 48 s[x][1]+=v[x][1]; 49 i=p[x]; 50 while (i) 51 { 52 if (jump[tt[i]][0]==x) 53 { 54 gao(tt[i]); 55 s[x][0]+=s[tt[i]][0]; 56 s[x][1]+=s[tt[i]][1]; 57 } 58 i=pre[i]; 59 } 60 if (s[x][0]) Flag[0]=1; 61 if (s[x][1]) Flag[1]=1; 62 if (s[x][1]>=2) Flag[0]=1; 63 } 64 int main() 65 { 66 int test; 67 scanf("%d",&test); 68 while (test--) 69 { 70 scanf("%d%d",&n,&m); 71 dp=0; 72 for (i=1;i<=n;i++) 73 p[i]=vis[i]=v[i][0]=v[i][1]=s[i][0]=s[i][1]=0; 74 Flag[0]=Flag[1]=0; 75 for (i=1;i<=m;i++) 76 { 77 flag[i]=0; 78 scanf("%d%d",&a[i],&b[i]); 79 link(a[i],b[i],i); 80 link(b[i],a[i],i); 81 } 82 for (i=1;i<=n;i++) 83 if (vis[i]==0) dfs(i,0); 84 for (i=1;i<=m;i++) 85 if (!flag[i]) 86 { 87 int c=lca(a[i],b[i]); 88 int dis=(deep[a[i]]+deep[b[i]]-2*deep[c]+1)%2; 89 v[jump[c][0]][dis]--; 90 v[c][dis]--; 91 v[a[i]][dis]++; 92 v[b[i]][dis]++; 93 } 94 for (i=1;i<=n;i++) 95 if (jump[i][0]==0) gao(i); 96 if (Flag[1]) 97 printf("YES\n");else printf("NO\n"); 98 if (Flag[0]) 99 printf("YES\n");else printf("NO\n"); 100 } 101 }
C.Segment
假设对于一个可行的方案,若Ai<Aj,且Bpi>Bpj,那么[Ai,Bpi]∩[Aj,Bpj]与[Ai,Bpj]∩[Aj,Bpi]是等价的,因此我们可以一直将这样的pi和pj进行交换,直到不存在这样的方案,也就是说对于所有的可行方案,经过这样的交换后,配对都会变成一样的,因此可以将A与B进行排序。若存在Ai>Bi则不存在可行方案,否则找一个最长的输出即可。
代码
1 #include<iostream> 2 #include<stdio.h> 3 #include<math.h> 4 #include<string.h> 5 #include<stdlib.h> 6 #include<algorithm> 7 using namespace std; 8 const int N= 1001010; 9 int n,m,i,a[N],b[N],f[N],j,flag,cnt,ans; 10 int main() 11 { 12 int test; 13 scanf("%d",&test); 14 while (test--) 15 { 16 ans=0;cnt=0;flag=0; 17 scanf("%d%d",&n,&m); 18 for (i=1;i<=n;i++) f[i]=0; 19 for (i=1;i<=m;i++) 20 scanf("%d",&a[i]); 21 for (i=1;i<=m;i++) 22 scanf("%d",&b[i]); 23 sort(a+1,a+1+m); 24 sort(b+1,b+1+m); 25 for (i=1;i<=m;i++) 26 { 27 for (j=a[i];j<=b[i];j++) 28 f[j]=1; 29 if (a[i]>b[i]) 30 { 31 flag=1;break; 32 } 33 } 34 for (i=1;i<=n;i++) 35 if (f[i]==0) cnt++;else 36 { 37 if (cnt>ans) ans=cnt;cnt=0; 38 } 39 if (cnt>ans) ans=cnt; 40 if (flag) printf("Stupid BrotherK!\n"); 41 else 42 printf("%d.000000\n",ans); 43 } 44 }
D.Brackets
题意是给一个括号序列,支持两种操作,第一种是修改一个位置的括号,第二种是查询区间[L,R]中,第k个未匹配括号是哪一个,输出下标。若无解输出-1
首先考虑有无解的判断,若一个序列中消除了已匹配的括号,那么未匹配括号必定是这种形式)))....)))(((((....((,我们可以根据前缀和来判断有无解,做法如下:
先将'(’视为1,')'视为-1,维护括号序列每个位置的的前缀和sum,那么在区间[L,R]中,未匹配括号的个数即为sum[L-1]+sum[R]-2*min(sum[i])(L<=i<=R),区间最小的前缀和可以用线段树维护一下。
然后考虑有解情况,若k<=sum[L-1]-sum[i],则答案为区间[l,r]中最靠左边的前缀和为sum[L-1]-k的括号的位置。否则为区间中最靠右边的,且前缀和为min(sum[i])+(k-(sum[L-1]-min(sum[i])))的括号的位置+1。
用线段树维护一下前缀和最小值,即可进行上面解决方法的所有操作,复杂度O(nlogn)
代码
1 #include<cstdio> 2 #include<set> 3 #define fi first 4 #define sc second 5 using namespace std; 6 typedef pair<int,int> P; 7 const int N = 2001010; 8 int v[N],e[N],n,m,i,o; 9 P s[N]; 10 char ch[N]; 11 P min(P a,P b) 12 { 13 if (a.fi==b.fi) 14 { 15 if (a.sc<b.sc) return a;return b; 16 } 17 if (a.fi<b.fi) return a;return b; 18 } 19 void clean(int x) 20 { 21 if (v[x]) 22 { 23 s[x].fi+=v[x]; 24 v[2*x]+=v[x]; 25 v[2*x+1]+=v[x]; 26 v[x]=0; 27 } 28 } 29 void gao(int x,int l,int r) 30 { 31 v[x]=0; 32 if (r-l==1) 33 { 34 s[x].fi=e[r]; 35 s[x].sc=r; 36 } 37 else 38 { 39 int m=(l+r)>>1; 40 gao(2*x,l,m); 41 gao(2*x+1,m,r); 42 s[x]=min(s[2*x],s[2*x+1]); 43 } 44 } 45 void change(int x,int a,int b,int l,int r,int c) 46 { 47 clean(x); 48 if ((a<=l)&&(r<=b)) 49 { 50 v[x]+=c; 51 return; 52 } 53 int m=(l+r)>>1; 54 if (a<m) change(2*x,a,b,l,m,c); 55 if (m<b) change(2*x+1,a,b,m,r,c); 56 clean(2*x);clean(2*x+1); 57 s[x]=min(s[2*x],s[2*x+1]); 58 } 59 P query(int x,int a,int b,int l,int r) 60 { 61 clean(x); 62 if ((a<=l)&&(r<=b)) return s[x]; 63 int m=(l+r)>>1; 64 P tmp;tmp.fi=1000000000; 65 if (a<m) tmp=min(tmp,query(2*x,a,b,l,m)); 66 if (m<b) tmp=min(tmp,query(2*x+1,a,b,m,r)); 67 return tmp; 68 } 69 int get(int x,int a,int b,int l,int r,int c,int typ) 70 { 71 clean(x); 72 if (s[x].fi>c) return 0; 73 int m=(l+r)>>1; 74 int ans=0; 75 if (r-l==1) 76 { 77 if (s[x].fi==c) return r;else return 0; 78 } 79 if ((a<=l)&&(r<=b)) 80 { 81 if (typ==0) 82 { 83 ans=get(2*x,a,b,l,m,c,typ); 84 if (ans) return ans; 85 ans=get(2*x+1,a,b,m,r,c,typ); 86 return ans; 87 } 88 else 89 { 90 ans=get(2*x+1,a,b,m,r,c,typ); 91 if (ans) return ans; 92 ans=get(2*x,a,b,l,m,c,typ); 93 return ans; 94 } 95 } 96 if (typ==0) 97 { 98 if (a<m) ans=get(2*x,a,b,l,m,c,typ); 99 if (ans) return ans; 100 if (m<b) ans=get(2*x+1,a,b,m,r,c,typ); 101 return ans; 102 } 103 else 104 { 105 if (m<b) ans=get(2*x+1,a,b,m,r,c,typ); 106 if (ans) return ans; 107 if (a<m) ans=get(2*x,a,b,l,m,c,typ); 108 return ans; 109 } 110 } 111 int main() 112 { 113 int test; 114 scanf("%d",&test); 115 while (test--) 116 { 117 scanf("%d%d",&n,&m); 118 for (i=1;i<=n;i++) 119 scanf(" %c",&ch[i]); 120 for (i=1;i<=n;i++) 121 if (ch[i]=='(') e[i+1]=e[i]+1;else e[i+1]=e[i]-1; 122 gao(1,0,n+1); 123 for (i=1;i<=m;i++) 124 { 125 int typ,a,b,c; 126 scanf("%d",&typ); 127 if (typ==1) 128 { 129 scanf("%d",&a); 130 if (ch[a]=='(') 131 { 132 ch[a]=')'; 133 a++; 134 change(1,a-1,n+1,0,n+1,-2); 135 } 136 else 137 { 138 ch[a]='('; 139 a++; 140 change(1,a-1,n+1,0,n+1,2); 141 } 142 } 143 else 144 { 145 scanf("%d%d%d",&a,&b,&c); 146 a++;b++; 147 P tmp=query(1,a-2,b,0,n+1); 148 149 int p=0,q=0; 150 p=query(1,a-2,a-1,0,n+1).fi; 151 q=query(1,b-1,b,0,n+1).fi; 152 153 if (p-tmp.fi+q-tmp.fi<c) 154 printf("-1\n"); 155 else 156 if (c<=p-tmp.fi) 157 printf("%d\n",get(1,a-1,n+1,0,n+1,p-c,0)-1); 158 else 159 printf("%d\n",get(1,0,b,0,n+1,tmp.fi+c-(p-tmp.fi)-1,1)); 160 } 161 } 162 } 163 }
E.Game
题意是有n个人站成环,有一个集合S,每次从集合S中随机选出一个数字s,然后依次报数,报道s的人从环中出去,问n轮后有哪些人有获胜的可能性。经典的约瑟夫问题,可以O(N^3)dp解决。
代码
1 #include<cstdio> 2 const int N = 1010; 3 int n,m,i,j,k,t,a[N],f[N][N],ans; 4 int main() 5 { 6 int test; 7 scanf("%d",&test); 8 while (test--) 9 { 10 scanf("%d%d",&n,&m); 11 for (i=1;i<=m;i++) 12 scanf("%d",&a[i]); 13 for (i=0;i<=n;i++) 14 for (j=0;j<=n;j++) 15 f[i][j]=0; 16 f[1][0]=1; 17 for (i=2;i<=n;i++) 18 { 19 for (j=1;j<=m;j++) 20 { 21 t=(a[j]-1)%i; 22 for (k=0;k<i;k++) 23 if (f[i-1][k]) 24 f[i][(t+1+k)%i]=1; 25 } 26 } 27 ans=0; 28 for (i=0;i<n;i++) 29 if (f[n][i]) ans++; 30 printf("%d\n",ans); 31 int flag=0; 32 for (i=0;i<n;i++) 33 if (f[n][i]) 34 { 35 if (flag) printf(" "); 36 flag=1; 37 printf("%d",i+1); 38 } 39 printf("\n"); 40 } 41 }
F.
G.
H.Occupation
题意是给一棵树,树上每个点都有一个权值,有三类操作,1.取一条路径上所有的没被取的点,2.如果x点被取了,则将其变为未取的状态,3.去以x为根的子树上所有没被取的点,每次操作后求出被取点的价值总和。
裸的树链剖分。。。
代码
1 #include<cstdio> 2 const int N = 1010101; 3 int dp,pre[N],p[N],tt[N],size[N],id[N],f[N],deep[N],go[N],L[N],R[N]; 4 int cnt,gf[N],i,n,e[N],a,b,v[N],s[N],sum[N]; 5 void link(int x,int y) 6 { 7 dp++;pre[dp]=p[x];p[x]=dp;tt[dp]=y; 8 } 9 void dfs1(int x,int y) 10 { 11 size[x]=1; 12 int i=p[x]; 13 while (i) 14 { 15 if (tt[i]!=y) 16 { 17 f[tt[i]]=x; 18 deep[tt[i]]=deep[x]+1; 19 dfs1(tt[i],x); 20 size[x]+=size[tt[i]]; 21 if (size[tt[i]]>size[go[x]]) go[x]=tt[i]; 22 } 23 i=pre[i]; 24 } 25 } 26 void dfs2(int x,int y) 27 { 28 L[x]=++cnt; 29 id[cnt]=x; 30 gf[x]=y; 31 if (go[x]) dfs2(go[x],y); 32 int i=p[x]; 33 while (i) 34 { 35 if (deep[tt[i]]>deep[x]) 36 if (tt[i]!=go[x]) 37 dfs2(tt[i],tt[i]); 38 i=pre[i]; 39 } 40 R[x]=cnt; 41 } 42 void clean(int x) 43 { 44 if (v[x]) 45 { 46 v[x]=0; 47 s[x]=sum[x]; 48 v[2*x]=1; 49 v[2*x+1]=1; 50 } 51 } 52 void change(int x,int a,int b,int l,int r,int c) 53 { 54 clean(x); 55 if ((a<=l)&&(r<=b)) 56 { 57 if (c) v[x]=1;else s[x]=0; 58 return; 59 } 60 int m=(l+r)>>1; 61 if (a<m) change(2*x,a,b,l,m,c); 62 if (m<b) change(2*x+1,a,b,m,r,c); 63 clean(2*x);clean(2*x+1); 64 s[x]=s[2*x]+s[2*x+1]; 65 } 66 void gao(int a,int b) 67 { 68 while (1) 69 { 70 if (deep[gf[a]]<deep[gf[b]]) a^=b^=a^=b; 71 if (gf[a]==gf[b]) 72 { 73 if (deep[a]<deep[b]) a^=b^=a^=b; 74 change(1,L[b]-1,L[a],0,n,1); 75 return; 76 } 77 else 78 { 79 change(1,L[gf[a]]-1,L[a],0,n,1); 80 a=f[gf[a]]; 81 } 82 } 83 } 84 void get(int x,int l,int r) 85 { 86 v[x]=s[x]=0; 87 if (r-l==1) 88 { 89 sum[x]=e[id[r]]; 90 return; 91 } 92 int m=(l+r)>>1; 93 get(2*x,l,m); 94 get(2*x+1,m,r); 95 sum[x]=sum[2*x]+sum[2*x+1]; 96 } 97 int main() 98 { 99 int test; 100 scanf("%d",&test); 101 while (test--) 102 { 103 scanf("%d",&n);dp=cnt=0; 104 for (i=1;i<=n;i++) 105 { 106 scanf("%d",&e[i]); 107 p[i]=go[i]=deep[i]=gf[i]=f[i]=0; 108 } 109 for (i=1;i<n;i++) 110 { 111 scanf("%d%d",&a,&b); 112 link(a,b);link(b,a); 113 } 114 dfs1(1,0); 115 dfs2(1,1); 116 get(1,0,n); 117 int m; 118 scanf("%d",&m); 119 for (i=1;i<=m;i++) 120 { 121 int typ; 122 scanf("%d",&typ); 123 if (typ==1) 124 { 125 scanf("%d%d",&a,&b); 126 gao(a,b); 127 } 128 else 129 { 130 scanf("%d",&a); 131 if (typ==2) 132 change(1,L[a]-1,L[a],0,n,0); 133 else 134 change(1,L[a]-1,R[a],0,n,1); 135 } 136 clean(1); 137 printf("%d\n",s[1]); 138 } 139 } 140 }
I.Exploration
题意是给一张图,其中有一些边为双向,有一些为单向,问是否存在一个起点,其能走出一条路径,至少能到达一个起点以外的点,且最终能回到起点,注意路一旦经过,就会坍塌。
可以先将双向边连接的点用并查集并起来,如果一个集合中的点不构成一颗树,则说明存在环,那么这个环就可以视为答案,输出YES,如果所有集合都是生成树,那么接着考虑有向边,如果有向边连接的两个点属于同一集合,则存在环经过该有向边,可以直接输出YES,否则则将两端点所在的集合连边,最后进行一边拓扑排序,判断是否存在环,如果存在环输出YES,否则输出NO
代码
1 #include<cstdio> 2 #include<queue> 3 using namespace std; 4 const int N = 2010101; 5 int f[N],n,m1,m2,l[N],r[N],i,a,b,id[N],cnt,rd[N]; 6 int dp,pre[N],p[N],tt[N],vis[N]; 7 queue<int> Q; 8 int gf(int x) 9 { 10 int t,p; 11 p=x; 12 while (p!=f[p]) p=f[p]; 13 while (x!=p) 14 { 15 t=f[x]; 16 f[x]=p; 17 x=t; 18 } 19 return p; 20 } 21 void link(int x,int y) 22 { 23 dp++;pre[dp]=p[x];p[x]=dp;tt[dp]=y; 24 } 25 int main() 26 { 27 int test; 28 scanf("%d",&test); 29 while (test--) 30 { 31 scanf("%d%d%d",&n,&m1,&m2); 32 for (i=1;i<=m1;i++) 33 scanf("%d%d",&l[i],&r[i]); 34 for (i=1;i<=m2;i++) 35 scanf("%d%d",&l[m1+i],&r[m1+i]); 36 dp=0; 37 for (i=1;i<=n;i++) f[i]=i,id[i]=rd[i]=p[i]=vis[i]=0; 38 for (i=1;i<=m1;i++) 39 { 40 if (l[i]==r[i]) continue; 41 a=gf(l[i]);b=gf(r[i]); 42 if (a==b) break; 43 f[a]=b; 44 } 45 if (i<=m1) 46 { 47 printf("YES\n"); 48 continue; 49 } 50 cnt=0; 51 for (i=1;i<=n;i++) 52 { 53 if (id[gf(i)]==0) id[gf(i)]=++cnt; 54 id[i]=id[gf(i)]; 55 } 56 for (i=m1+1;i<=m1+m2;i++) 57 { 58 if (l[i]==r[i]) continue; 59 if (id[l[i]]==id[r[i]]) break; 60 link(id[l[i]],id[r[i]]); 61 rd[id[r[i]]]++; 62 } 63 if(i<=m1+m2) 64 { 65 printf("YES\n"); 66 continue; 67 } 68 for (i=1;i<=cnt;i++) 69 if (rd[i]==0) Q.push(i); 70 while (!Q.empty()) 71 { 72 int x=Q.front();Q.pop(); 73 i=p[x]; 74 while (i) 75 { 76 rd[tt[i]]--; 77 if (!rd[tt[i]]) Q.push(tt[i]); 78 i=pre[i]; 79 } 80 } 81 for (i=1;i<=cnt;i++) 82 if (rd[i]) break; 83 if (i<=cnt) printf("YES\n");else printf("NO\n"); 84 } 85 }
J.GCD
题意是给一些询问,每次询问区间[L,R]的gcd是多少,且得到的答案为w,问能否构造出一个使得所有询问都满足的答案,如果存在多个方案,输出一个序列和最小的方案。
一开始设序列所有数字为1,对于一个询问[L,R]我们可以把区间[L,R]中的数字ai变为lcm(ai,w),这样就可以构造出一个序列,最后用询问来验证一下这个序列是否合法,如果合法,那么该序列即为最小方案,若不合法即为无解。
代码
1 #include<cstdio> 2 const int N = 101010; 3 int n,m,i,j,l[N],r[N],w[N],test,tmp; 4 long long a[N]; 5 long long gcd(long long a,long long b) 6 { 7 if (b==0) return a; 8 return gcd(b,a%b); 9 } 10 int main() 11 { 12 scanf("%d",&test); 13 while (test--) 14 { 15 scanf("%d%d",&n,&m); 16 for (i=1;i<=m;i++) 17 scanf("%d%d%d",&l[i],&r[i],&w[i]); 18 for (i=1;i<=n;i++) 19 { 20 a[i]=1; 21 for (j=1;j<=m;j++) 22 if ((l[j]<=i)&&(i<=r[j])) 23 { 24 a[i]=(a[i]*w[j])/gcd(a[i],w[j]); 25 if (a[i]>1000000000) break; 26 } 27 if (a[i]>1000000000) break; 28 } 29 if (i<=n) 30 printf("Stupid BrotherK!\n"); 31 else 32 { 33 for (i=1;i<=m;i++) 34 { 35 tmp=0; 36 for (j=l[i];j<=r[i];j++) 37 tmp=gcd(tmp,a[j]); 38 if (tmp!=w[i]) break; 39 } 40 if (i<=m) printf("Stupid BrotherK!\n"); 41 else 42 { 43 for (i=1;i<n;i++) 44 printf("%I64d ",a[i]);printf("%I64d\n",a[n]); 45 } 46 } 47 } 48 }