The 2013 ACMICPC Asia Regional Chengdu
还有19天出发北京站,今年北京站的出题方是上交,去年他们出的成都现场的赛题,首先复盘一下。
去年的成都是我经历的第一次现场赛,也是近距离第一次见到了CLJ的真人,最后也是被虐惨了,那时候是声闻大神带着我们去的,也是在那次现场之后,深深地感受到了差距。现在我们进步了,只可惜选手都在发展,比赛也在发展,别人大概是进步得更多吧,上场西安赛站也只能遗憾。
没想到最后一场居然又能碰到开场第一次能够遇上的出题方,也是个奇妙的巧合吧。
【B】模拟
【C】-_-///
【E】计算几何
【G】在线AC自动机
【I】模拟(用STL中的set)
----------------------------------------------------
Assignment For Princess
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Special Judge
1 6 8
【Sample Output】
Case #1: 1 2 1 2 3 2 2 4 3 3 4 4 4 5 5 5 6 7 5 1 6 6 1 8
【Hint】
The restrictions like N >= 10 will be too big for a sample. So the sample is just a simple case for the detailed formats of input and output,
and it may be helpful for a better understanding. Anyway it won’t appear in actual test cases.
1 /* *********************************************** 2 MYID : Chen Fan 3 LANG : G++ 4 PROG : HDU 4781 5 ************************************************ */ 6 7 #include <iostream> 8 #include <cstdio> 9 #include <cstring> 10 11 using namespace std; 12 13 int mapl[81][81],dist[81][81]; 14 bool use[81]; 15 16 int main() 17 { 18 int t,n,m; 19 scanf("%d",&t); 20 for (int tt=1;tt<=t;tt++) 21 { 22 printf("Case #%d:\n",tt); 23 scanf("%d%d",&n,&m); 24 int tot=0; 25 bool bq=false; 26 memset(mapl,0,sizeof(mapl)); 27 memset(dist,-1,sizeof(dist)); 28 29 for (int i=1;i<n;i++) 30 { 31 tot+=i; 32 mapl[i][i+1]=i; 33 dist[i][i+1]=i; 34 } 35 36 for (int i=n;i<n+3;i++) 37 if ((tot+i)%3==0) 38 { 39 mapl[n][1]=i; 40 dist[n][1]=i; 41 break; 42 } 43 44 memset(use,0,sizeof(use)); 45 use[mapl[n][1]]=true; 46 47 for (int i=1;i<=n;i++) 48 for (int j=1;j<=n;j++) 49 if (i!=j) 50 for (int k=1;k<=n;k++) 51 if (i!=k&&j!=k) 52 if (dist[j][i]>0&&dist[i][k]>0) 53 if (dist[j][k]==-1) dist[j][k]=dist[j][i]+dist[i][k]; 54 55 for (int i=1;i<=n;i++) 56 for (int j=1;j<=n;j++) 57 if (dist[i][j]>-1) dist[i][j]%=3; 58 59 //for (int i=1;i<=n;i++) 60 //for (int j=1;j<=n;j++) printf("%d %d %d\n",i,j,dist[i][j]); 61 62 for (int i=n;i<=m;i++) 63 { 64 if (!use[i]) 65 { 66 for (int j=1;j<=n;j++) 67 for (int k=1;k<=n;k++) 68 if (!use[i]) 69 if (mapl[j][k]==0&&mapl[k][j]==0&&dist[j][k]>-1&&dist[j][k]%3==i%3) 70 { 71 mapl[j][k]=i; 72 use[i]=true; 73 } 74 } 75 if (!use[i]) 76 { 77 printf("-1\n"); 78 bq=true; 79 break; 80 } 81 } 82 83 if (!bq) 84 for (int i=1;i<=n;i++) 85 for (int j=1;j<=n;j++) 86 if (mapl[i][j]>0) printf("%d %d %d\n",i,j,mapl[i][j]); 87 } 88 89 return 0; 90 }
Dinner Coming Soon
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 102400/102400 K (Java/Others)2 3 2 1 2 10 6 -1 1 -1 -1 5 -1 1 2 1 0 2 3 1 1 2 2 1 2 5 5 -1 -1 -1 -1 1 2 10 2 1 2 2 10
【Sample Output】
Case #1: 17 Case #2: Forever Alone
【题意】
给出一张有向图,主人公需要在时间T之内从编号为1的点走到编号为n的点。图中存在一个货币系统,主人公在初始状态的时候身上有R单位的货币,在图中除了1和n之外的所有点都可以买进或者卖出一包盐,每一个点的盐价不同,因此主人公可以通过这种方式在不同的位置进行盐的买卖赚取收益。图中的每一条有向边都有两个代价,时间花费和货币花费,而且除此之外这张图中还有K个“平行世界”,分别标记为0~K-1,在每个不同的平行世界中每个点的盐价也不同,主人公可以花费1单位时间从第i个平行世界的某个点中跳跃到第(i+1)%K个平行世界的同一个点上,唯一的限制是1点和n点只能从第0个平行世界中进入,且一旦到达第n点,这次遍历就结束了。
现在,最终的问题就是主人公从1点出发走到n点,身上最多可能持有多少单位的货币。
【分析】
写这道题的时候代码遭遇神坑啊!!!!
首先构图非常简单,相对于一般的图,本题只是把普通的单一代价有向边再加上一个代价,平行世界的跳转只是从i跳到(i+1)%k,且花费只有1单位时间。对于图中每一个点的情况,很容易能够想到状态转移方程式,用f(i,j,k,l)表示当前在第i点,花费了j时间,手上持有k包盐,目前在平行世界k的最大持有货币数。出现的位置状态转移只有两种:沿着有向边走向下一个点或者进行平行世界跳转。位置转移完成之后,接下来发生的价值转移有三种,不作任何操作、买入一包盐或者卖出一包盐。大致的方程是这样的,这里省略了一些转移条件:
f(i,j,k,l)=max{ f(p,j+edge[i,p].time,k,l),
f(p,j+edge[i,p].time,k+1,l)-price[l][p],
f(p,j+edge[i,p].time,k-1,l)+price[l][p],
f(i,j+1,k,(l+1)%K),
f(i,j+1,k+1,(l+1)%K)-price[(l+1)%K][i],
f(i,j+1,k-1,(l+1)%K)+price[(l+1)%K][i] }
最直接的想法是使用SPFA作为整体的DP框架,虽然整体数据范围不太大,但是这里反复进出队列的话,超时的可能性非常大。这里DP遇到的最大问题是后效性,关于这一点,我们可以看到状态递推的方向是花费时间增加的方向。普通的SPFA不能保证后面加入队列的状态时间一定是递增的,因此需要反复多次进出队列,所以在这里使用一个花费时间递增的优先队列来替换掉SPFA中的普通队列,保证状态按照花费时间递增的顺序来扩展,就能解决DP的后效性问题,记录一下加入过队列的点不用再次加入即可节省下来大量的时间。
写代码的时候因为一点失误被坑了好久......吸取的教训是:DP中后续的六种状态的值必须从原始状态出发!其实这个原本是非常直观的,写到图论的结构里面之后因为代码太多了,居然简单地就直接从1往2、3写,从4往5、6写了。
1 /* *********************************************** 2 MYID : Chen Fan 3 LANG : G++ 4 PROG : HDU4784 5 ************************************************ */ 6 7 #include <iostream> 8 #include <cstdio> 9 #include <cstring> 10 #include <algorithm> 11 #include <queue> 12 13 using namespace std; 14 15 typedef struct nod{ 16 int a,b,c,d; 17 } node; 18 19 int start[110],num[110]; 20 int p[10][110]; 21 int n,m,b,k,r,tim; 22 node edge[210]; 23 24 typedef struct qnod 25 { 26 int s,t,b,k; 27 friend bool operator < (qnod a,qnod b) 28 { 29 return a.t>b.t; 30 } 31 } qnode; 32 33 int f[110][210][10][10]; 34 35 bool op(node a,node b) 36 { 37 return a.a<b.a; 38 } 39 40 int main() 41 { 42 freopen("test.txt","r",stdin); 43 44 int t; 45 scanf("%d",&t); 46 for (int tt=1;tt<=t;tt++) 47 { 48 scanf("%d%d%d%d%d%d",&n,&m,&b,&k,&r,&tim); 49 for (int i=0;i<k;i++) 50 for (int j=1;j<=n;j++) scanf("%d",&p[i][j]); 51 for (int i=1;i<=m;i++) scanf("%d%d%d%d",&edge[i].a,&edge[i].b,&edge[i].c,&edge[i].d); 52 sort(&edge[1],&edge[m+1],op); 53 54 int o=0; 55 memset(num,0,sizeof(num)); 56 for (int i=1;i<=m;i++) 57 { 58 if (o!=edge[i].a) 59 { 60 o=edge[i].a; 61 start[o]=i; 62 } 63 num[o]++; 64 } 65 66 printf("Case #%d: ",tt); 67 68 priority_queue<qnode> q; 69 while (!q.empty()) q.pop(); 70 memset(f,-1,sizeof(f)); 71 72 qnode st; 73 st.s=1;st.t=0;st.b=0;st.k=0; 74 f[1][0][0][0]=r; 75 q.push(st); 76 77 int ans=-1; 78 while (!q.empty()) 79 { 80 qnode now=q.top(); 81 q.pop(); 82 83 for (int i=0;i<num[now.s];i++) 84 { 85 qnode next; 86 int mm=f[now.s][now.t][now.b][now.k]-edge[start[now.s]+i].d; 87 next.t=now.t+edge[start[now.s]+i].c; 88 if (mm<0||next.t>tim) continue; 89 next.s=edge[start[now.s]+i].b; 90 next.b=now.b; 91 next.k=now.k; 92 93 if (next.s==1&&next.k!=0) continue; 94 if (next.s==n) 95 { 96 if (next.k!=0) continue; 97 if (ans<mm) ans=mm; 98 } else 99 { 100 if (f[next.s][next.t][next.b][next.k]<0) q.push(next); 101 if (f[next.s][next.t][next.b][next.k]<mm) f[next.s][next.t][next.b][next.k]=mm; 102 103 if (next.s==1) continue; 104 if (next.b<b&&f[next.s][next.t][next.b][next.k]>=p[next.k][next.s]) 105 { 106 qnode temp=next; 107 temp.b++; 108 mm=f[now.s][now.t][now.b][now.k]-edge[start[now.s]+i].d-p[next.k][next.s]; //!!!!! 109 if (f[temp.s][temp.t][temp.b][temp.k]<0) q.push(temp); 110 if (f[temp.s][temp.t][temp.b][temp.k]<mm) f[temp.s][temp.t][temp.b][temp.k]=mm; 111 } 112 113 if (next.b>0) 114 { 115 qnode temp=next; 116 temp.b--; 117 mm=f[now.s][now.t][now.b][now.k]-edge[start[now.s]+i].d+p[next.k][next.s]; 118 if (f[temp.s][temp.t][temp.b][temp.k]<0) q.push(temp); 119 if (f[temp.s][temp.t][temp.b][temp.k]<mm) f[temp.s][temp.t][temp.b][temp.k]=mm; 120 } 121 } 122 } 123 if (now.s==1) continue; 124 qnode next=now; 125 int mm=f[now.s][now.t][now.b][now.k]; 126 next.k=(next.k+1)%k; 127 next.t++; 128 if (next.t>tim) continue; 129 130 if (f[next.s][next.t][next.b][next.k]<0) q.push(next); 131 if (f[next.s][next.t][next.b][next.k]<mm) f[next.s][next.t][next.b][next.k]=mm; 132 133 if (next.b<b&&f[next.s][next.t][next.b][next.k]>=p[next.k][next.s]) 134 { 135 qnode temp=next; 136 temp.b++; 137 mm=f[now.s][now.t][now.b][now.k]-p[next.k][next.s]; 138 if (f[temp.s][temp.t][temp.b][temp.k]<0) q.push(temp); 139 if (f[temp.s][temp.t][temp.b][temp.k]<mm) f[temp.s][temp.t][temp.b][temp.k]=mm; 140 } 141 142 if (next.b>0) 143 { 144 qnode temp=next; 145 temp.b--; 146 mm=f[now.s][now.t][now.b][now.k]+p[next.k][next.s]; 147 if (f[temp.s][temp.t][temp.b][temp.k]<0) q.push(temp); 148 if (f[temp.s][temp.t][temp.b][temp.k]<mm) f[temp.s][temp.t][temp.b][temp.k]=mm; 149 } 150 } 151 152 if (ans<0) printf("Forever Alone\n"); 153 else printf("%d\n",ans); 154 } 155 156 return 0; 157 }
Fibonacci Tree
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
2 4 4 1 2 1 2 3 1 3 4 1 1 4 0 5 6 1 2 1 1 3 1 1 4 1 1 5 1 3 5 1 4 2 1
【Sample Output】
Case #1: Yes Case #2: No
【题意】
给出一张无向图,图中每一条边都有黑或者白两种颜色。询问是否可以从这张图中找到这样一个生成树,使得白边的条数为一个斐波那契数。
【分析】
考虑使用Kruskal构造最小生成树的方法,如果把颜色作为边权值,则可以构造出白边优先或者黑边优先的生成树。
白边优先树是尽可能多的取白边,能得到最多的使生成树成立的白边数,黑边优先树则是尽可能多的取黑边,得到最少的使生成树成立的白边数。从白边优先树出发的话,如果我们取掉一条白边,添加一条黑边,则构成的新图一定还是一棵树。即根据这个原理,可以构造出白边数介于最少白边数和最大白边数之间的所有生成树。接下来只要判断一下介于这两个数之间是不是存在斐波那契数即可。
1 /* *********************************************** 2 MYID : Chen Fan 3 LANG : G++ 4 PROG : HDU4786 5 ************************************************ */ 6 7 #include <iostream> 8 #include <cstdio> 9 #include <cstring> 10 #include <algorithm> 11 #include <bitset> 12 13 typedef struct nod 14 { 15 int a,b,color; 16 } node; 17 18 node edge[200010]; 19 20 using namespace std; 21 22 bool op(node a,node b) 23 { 24 return a.color<b.color; 25 } 26 27 int n,m; 28 29 int father[100010]; 30 31 int getfather(int x) 32 { 33 if (father[x]!=x) father[x]=getfather(father[x]); 34 return father[x]; 35 } 36 37 void link(int x,int y) 38 { 39 father[getfather(x)]=getfather(y); 40 } 41 42 int kruskal1() 43 { 44 for (int i=1;i<=n;i++) father[i]=i; 45 int num=0,tot=0; 46 for (int i=1;i<=m;i++) 47 if (getfather(edge[i].a)!=getfather(edge[i].b)) 48 { 49 link(edge[i].a,edge[i].b); 50 if (edge[i].color) num++; 51 tot++; 52 } 53 if (tot!=n-1) return -1; 54 else return num; 55 } 56 57 int kruskal2() 58 { 59 for (int i=1;i<=n;i++) father[i]=i; 60 int num=0,tot=0; 61 for (int i=m;i>=1;i--) 62 if (getfather(edge[i].a)!=getfather(edge[i].b)) 63 { 64 link(edge[i].a,edge[i].b); 65 if (edge[i].color) num++; 66 tot++; 67 } 68 if (tot!=n-1) return -1; 69 else return num; 70 } 71 72 int main() 73 { 74 int tot=0; 75 bitset<100010>fbn; 76 fbn.reset(); 77 int a=1,b=1,c; 78 fbn[1]=1; 79 while (1) 80 { 81 c=a+b; 82 if (c>=100010) break; 83 fbn[c]=1; 84 a=b; 85 b=c; 86 } 87 88 int t; 89 scanf("%d",&t); 90 for (int tt=1;tt<=t;tt++) 91 { 92 scanf("%d%d",&n,&m); 93 94 for (int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].color); 95 96 printf("Case #%d: ",tt); 97 98 sort(&edge[1],&edge[m+1],op); 99 int b1=kruskal1(); 100 101 if (b1<0) 102 { 103 printf("No\n"); 104 continue; 105 } 106 107 int b2=kruskal2(); 108 109 bool done=false; 110 for (int i=b1;i<=b2;i++) 111 if (fbn[i]) 112 { 113 done=true; 114 break; 115 } 116 117 if (done) printf("Yes\n"); 118 else printf("No\n"); 119 } 120 121 return 0; 122 }
Hard Disk Drive
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
2 100[MB] 1[B]
【Sample Output】
Case #1: 4.63% Case #2: 0.00%
【Hint】
【分析】
签到题
Just Random
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
4 0 5 0 5 3 0 0 999999 0 999999 1000000 0 0 3 0 3 8 7 3 3 4 4 7 0
【Sample Output】
Case #1: 1/3 Case #2: 1/1000000 Case #3: 0/1 Case #4: 1/1
【题意】
给定两个区间,每次分别从这两个区间中取出一个数求和,问有多少种组合的和是能够对p取模之后等于m的概率。
【分析】