2015 Multi-University Training Contest 3
1001 Magician
线段树。根据奇偶性分成4个区间。维护子列和最大值。
想法很简单。但是并不好写。
首先初始化的时候对于不存在的点要写成-INF。
然后pushup的时候。对于每个区间要考虑四个情况。
例如sum01。
他可能是左子树的sum01右子树的sum01。
或者左子树的sum01+右子树的sum01。
或者左子树的sum00+右子树的sum11。
最后注意query函数的写法。
如果在递归的时候就讨论奇偶性。
下层的每个区间都要被重复调用。而且是层数的指数级。
于是参考学习了一种直接把子区间合并成一个区间类型返回的方法。
合成目标区间后再讨论奇偶性。
【其实pushup和query可以写好看些。好不容易调对。懒得改了。】
1 # include <iostream> 2 # include <cstdio> 3 # include <algorithm> 4 using namespace std; 5 typedef long long LL; 6 # define maxn 100010 7 # define INF (LL)1000000000000 8 LL a[maxn]; 9 10 struct node 11 { 12 int l,r; 13 LL sum00,sum01,sum10,sum11; 14 }tree[4*maxn]; 15 16 void pushup(int i) 17 { 18 tree[i].sum00=tree[i].sum01=tree[i].sum10=tree[i].sum11=-INF; 19 tree[i].sum00=max(tree[i].sum00,tree[2*i].sum00+tree[2*i+1].sum10); 20 tree[i].sum00=max(tree[i].sum00,tree[2*i].sum01+tree[2*i+1].sum00); 21 tree[i].sum00=max(tree[i].sum00,tree[2*i].sum00); 22 tree[i].sum00=max(tree[i].sum00,tree[2*i+1].sum00); 23 tree[i].sum01=max(tree[i].sum01,tree[2*i].sum00+tree[2*i+1].sum11); 24 tree[i].sum01=max(tree[i].sum01,tree[2*i].sum01+tree[2*i+1].sum01); 25 tree[i].sum01=max(tree[i].sum01,tree[2*i].sum01); 26 tree[i].sum01=max(tree[i].sum01,tree[2*i+1].sum01); 27 tree[i].sum10=max(tree[i].sum10,tree[2*i].sum10+tree[2*i+1].sum10); 28 tree[i].sum10=max(tree[i].sum10,tree[2*i].sum11+tree[2*i+1].sum00); 29 tree[i].sum10=max(tree[i].sum10,tree[2*i].sum10); 30 tree[i].sum10=max(tree[i].sum10,tree[2*i+1].sum10); 31 tree[i].sum11=max(tree[i].sum11,tree[2*i].sum10+tree[2*i+1].sum11); 32 tree[i].sum11=max(tree[i].sum11,tree[2*i].sum11+tree[2*i+1].sum01); 33 tree[i].sum11=max(tree[i].sum11,tree[2*i].sum11); 34 tree[i].sum11=max(tree[i].sum11,tree[2*i+1].sum11); 35 return; 36 } 37 38 void buildtree(int i,int l,int r) 39 { 40 tree[i].l=l; tree[i].r=r; 41 if(l<r) 42 { 43 buildtree(2*i,l,(l+r)/2); 44 buildtree(2*i+1,(l+r)/2+1,r); 45 pushup(i); 46 } 47 else 48 { 49 if(l%2) 50 { 51 tree[i].sum11=a[l]; 52 tree[i].sum10=tree[i].sum01=tree[i].sum00=-INF; 53 } 54 else 55 { 56 tree[i].sum00=a[l]; 57 tree[i].sum10=tree[i].sum01=tree[i].sum11=-INF; 58 } 59 } 60 return; 61 } 62 63 void update(int i,int p,int v) 64 { 65 if(tree[i].l==tree[i].r) 66 { 67 if(p%2) tree[i].sum11=v; 68 else tree[i].sum00=v; 69 } 70 else 71 { 72 if(p<=(tree[i].l+tree[i].r)/2) update(2*i,p,v); 73 else update(2*i+1,p,v); 74 pushup(i); 75 } 76 return; 77 } 78 79 node query(int i,int l,int r) 80 { 81 if(tree[i].l>=l&&tree[i].r<=r) return tree[i]; 82 if(r<=(tree[i].l+tree[i].r)/2) return query(2*i,l,r); 83 if(l>=(tree[i].l+tree[i].r)/2+1) return query(2*i+1,l,r); 84 node t,t1,t2; 85 t1=query(2*i,l,r); 86 t2=query(2*i+1,l,r); 87 t.sum00=max(t1.sum00,t2.sum00); 88 t.sum00=max(t.sum00,t1.sum00+t2.sum10); 89 t.sum00=max(t.sum00,t1.sum01+t2.sum00); 90 t.sum01=max(t1.sum01,t2.sum01); 91 t.sum01=max(t.sum01,t1.sum00+t2.sum11); 92 t.sum01=max(t.sum01,t1.sum01+t2.sum01); 93 t.sum10=max(t1.sum10,t2.sum10); 94 t.sum10=max(t.sum10,t1.sum10+t2.sum10); 95 t.sum10=max(t.sum10,t1.sum11+t2.sum00); 96 t.sum11=max(t1.sum11,t2.sum11); 97 t.sum11=max(t.sum11,t1.sum10+t2.sum11); 98 t.sum11=max(t.sum11,t1.sum11+t2.sum01); 99 return t; 100 } 101 102 int main(void) 103 { 104 int T; cin>>T; 105 while(T--) 106 { 107 int n,m; scanf("%d%d",&n,&m); 108 for(int i=1;i<=n;i++) scanf("%I64d",a+i); 109 buildtree(1,1,n); 110 while(m--) 111 { 112 int type,x,y; scanf("%d%d%d",&type,&x,&y); 113 if(type) update(1,x,y); 114 else 115 { 116 node tem=query(1,x,y); 117 LL ans=-INF; 118 ans=max(ans,tem.sum00); 119 ans=max(ans,tem.sum01); 120 ans=max(ans,tem.sum10); 121 ans=max(ans,tem.sum11); 122 printf("%I64d\n",ans); 123 } 124 } 125 } 126 return 0; 127 }
1002 RGCDQ
先枚举1000000内所有质因数。
然后统计区间内每个数的质因数个数。
注意到最大只有7。
对于因子个数为1-7的数的个数处理一下前缀和。
对于每个询问。算区间内因子数分别为1-7的数的个数。
找到最大的gcd就是答案。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 # include <vector> 6 using namespace std; 7 # define maxn 1000010 8 bool tag[maxn]={1,1}; 9 int prime[42000],cnt[maxn]={0},presum[8][maxn]={0}; 10 11 int main(void) 12 { 13 int t=0; 14 for(int i=2;i<maxn/2;i++) 15 { 16 if(!tag[i]) prime[++t]=i; 17 for(int j=1;j<=t&&i*prime[j]<maxn;j++) 18 { 19 tag[i*prime[j]]=1; 20 if(i%prime[j]==0) break; 21 } 22 } 23 for(int i=1;i<=t;i++) 24 for(int j=1;prime[i]*j<=maxn;j++) 25 cnt[prime[i]*j]++; 26 for(int i=1;i<=maxn;i++) 27 for(int j=1;j<=7;j++) 28 { 29 presum[j][i]=presum[j][i-1]; 30 if(cnt[i]==j) presum[j][i]++; 31 } 32 int T; cin>>T; 33 while(T--) 34 { 35 int l,r; scanf("%d%d",&l,&r); 36 int mark[8]={0}; 37 for(int i=1;i<8;i++) mark[i]=max(0,presum[i][r]-presum[i][l-1]); 38 if(mark[7]>=2) printf("7\n"); 39 else if(mark[6]>=2) printf("6\n"); 40 else if(mark[5]>=2) printf("5\n"); 41 else if(mark[4]>=2) printf("4\n"); 42 else if(mark[3]>=2||mark[3]&&mark[6]) printf("3\n"); 43 else if(mark[2]>=2||mark[2]&&mark[4]||mark[2]&&mark[6]) printf("2\n"); 44 else printf("1\n"); 45 } 46 return 0; 47 }
1004 Painter
看成R和B的两张图。
统计R\斜边上与B/斜边上的连续染色段数即为答案。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 using namespace std; 5 # define CLR(x) memset(x,0,sizeof(x)) 6 char map[55][55]; 7 int R[55][55],B[55][55]; 8 9 int main(void) 10 { 11 int T; cin>>T; 12 while(T--) 13 { 14 CLR(map); CLR(R); CLR(B); 15 int n; scanf("%d",&n); 16 for(int i=0;i<n;i++) scanf("%s",map[i]); 17 int len=strlen(map[0]); 18 for(int i=0;i<n;i++) 19 for(int j=0;j<len;j++) 20 { 21 if(map[i][j]=='R') R[i][j]=1; 22 else if(map[i][j]=='B') B[i][j]=1; 23 else if(map[i][j]=='G') R[i][j]=B[i][j]=1; 24 } 25 int ans=0; 26 for(int i=1-n;i<len;i++) 27 { 28 int flag=0; 29 for(int j=0;j<n&&i+j<len;j++) 30 { 31 if(i+j<0) continue; 32 if(!flag&&R[j][i+j]) {ans++;flag=1;} 33 if(!R[j][i+j]) flag=0; 34 } 35 } 36 for(int i=0;i<n+len-1;i++) 37 { 38 int flag=0; 39 for(int j=0;j<len&&i-j>=0;j++) 40 { 41 if(i-j>=n) continue; 42 if(!flag&&B[i-j][j]) {ans++;flag=1;} 43 if(!B[i-j][j]) flag=0; 44 } 45 } 46 printf("%d\n",ans); 47 } 48 return 0; 49 }
1005 Fan Li
1006 Beautiful Set
1007 Hope
1008 Solve this interesting problem
暴搜可行 证明不会。
l<0跳。l>r-l跳。r>现有答案跳。
搜[l,2*r-l]的时候注意死循环。
1 # include <iostream> 2 # include <cstdio> 3 # include <algorithm> 4 using namespace std; 5 typedef long long LL; 6 LL ans; 7 8 void dfs(LL l,LL r) 9 { 10 if(l==0) 11 { 12 if(ans==-1) ans=r; 13 else ans=min(ans,r); 14 return; 15 } 16 if(l<0) return; 17 if(ans!=-1&&r>=ans) return; 18 if(r>2*l) return; 19 if(r>l) dfs(l,2*r-l); 20 dfs(2*l-r-1,r); 21 dfs(2*l-r-2,r); 22 dfs(l,2*r-l+1); 23 return; 24 } 25 26 int main(void) 27 { 28 LL l,r; 29 while((scanf("%I64d%I64d",&l,&r))!=EOF) 30 { 31 ans=-1; 32 dfs(l,r); 33 printf("%I64d\n",ans); 34 } 35 return 0; 36 }
1009 Boring Class
1010 Crazy Bobo
按官方题解。
把每条边变成从w大的点到w小的点的有像边。
以u为min的目标集就是能走到u的点的集合。
花了较长的时间思考这个问题。
换一种说法。
其实就是对于一个已经确定最小值u的目标集S。
点v在S中当且仅当v能通过递减的路到达u。
简要说明一下必要性和充分性。
随手画了个图。
任意取一条链。比方X。它必然有一个端点x1。
只要说明离x1最近的点x2小于x1。然后考虑去掉x1的图。归纳即可得到上述结论。
考虑S中比x1小的最大的点p的位置。
如果x2就是p。那么它比x1小。
如果x2不是p。那么由于x2离x1最近。无论p在什么位置。x2都在p-x1的链上。
根据S的定义。x2小于x1。
反之。如果所有链都是以递减的顺序通向u的。
那么考虑图中的最大点。其必然在一条链的端点。
比方仍取X链。x1是最大点。
最大点给集合带来的约束条件只有一条。
那就是最大点到次大点的路径上的所有点小于最大点。
但是所有点都小于最大点。所以这个条件必然成立。
也就是说原来的点属于S集等价于去掉最大点后的所有点属于S集合。
而去掉x1后的最大点可能是x2或者是其他链的端点。
只要不断从端点去掉最大点。归纳即可证明原来的所有点属于S集。
证明这个之后。树dp即可。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 # include <vector> 6 using namespace std; 7 # define maxn 500050 8 int w[maxn],dp[maxn]; 9 vector<int> edge[maxn]; 10 11 struct V 12 { 13 int id,w; 14 } node[maxn]; 15 16 bool cmp(V a,V b) 17 { 18 return a.w>b.w; 19 } 20 21 int main(void) 22 { 23 int n; 24 while((scanf("%d",&n))!=EOF) 25 { 26 memset(edge,0,sizeof(edge)); 27 for(int i=1;i<=n;i++) 28 { 29 scanf("%d",w+i); 30 node[i].id=i; 31 node[i].w=w[i]; 32 } 33 for(int i=1;i<n;i++) 34 { 35 int a,b; scanf("%d%d",&a,&b); 36 if(w[a]>w[b]) edge[b].push_back(a); 37 else if(w[a]<w[b]) edge[a].push_back(b); 38 } 39 sort(node+1,node+1+n,cmp); 40 int ans=0; 41 for(int i=1;i<=n;i++) 42 { 43 int pos=node[i].id; 44 dp[pos]=1; 45 for(int j=0;j<edge[pos].size();j++) 46 dp[pos]+=dp[edge[pos][j]]; 47 ans=max(dp[pos],ans); 48 } 49 printf("%d\n",ans); 50 } 51 return 0; 52 }
1011 Work
签到题。dfs一遍即可。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <vector> 5 using namespace std; 6 int ind[105],cnt[105]; 7 vector <int> edge[105]; 8 9 void dfs(int x) 10 { 11 cnt[x]=edge[x].size(); 12 for(int i=0;i<edge[x].size();i++) 13 { 14 dfs(edge[x][i]); 15 cnt[x]+=cnt[edge[x][i]]; 16 } 17 return; 18 } 19 20 int main(void) 21 { 22 int n,k; 23 while((scanf("%d%d",&n,&k))!=EOF) 24 { 25 memset(ind,0,sizeof(ind)); 26 memset(cnt,0,sizeof(cnt)); 27 for(int i=1;i<=n;i++) edge[i].clear(); 28 for(int i=1;i<n;i++) 29 { 30 int u,v; scanf("%d%d",&u,&v); 31 edge[u].push_back(v); 32 ind[v]++; 33 } 34 for(int i=1;i<=n;i++) if(!ind[i]) dfs(i); 35 int ans=0; 36 for(int i=1;i<=n;i++) if(cnt[i]==k) ans++; 37 printf("%d\n",ans); 38 } 39 return 0; 40 }