Codeforces Round #264 (Div. 2) 解题报告

Source:  http://codeforces.com/contest/463

打得比较差。。第三题想写nlgn的,结果调了很久又wa,以为写挫,过了很久发现做法有问题。。最后两题惨淡收场。第四题题解两个做法都想到了,但都差一点。。第五题干脆就没看过题目,今天tle无数次才发现问题。。

 

463A Caisa and Sugar

题意:去超市买sugar,n种sugar,只买一个(不是一种),自己有s美刀,每种sugar需要x美刀y美分,超市换零钱的时候美分全部用糖果代替,问最多能得到多少糖果。

分析:签到题,但是题面描述不清,只买一单元的sugar,而不是买一种,因此贡献了一次WA,最后过第二题的比这题还多。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 int n, s, x, y;
 7 int main()
 8 {
 9     while(scanf("%d %d", &n, &s) != EOF)
10     {
11         int ans = -1, tmp, need;
12         for (int i = 0; i < n; i++){
13             scanf("%d %d", &x, &y);
14             need = x * 100 + y;
15             tmp = s * 100;
16         //    while(tmp >= need){
17             if (tmp >= need){
18                 tmp -= need;
19                 if (tmp % 100 > ans) ans = tmp % 100;
20             }
21         }
22         printf("%d\n", ans);
23     }
24     return 0;
25 }
View Code

 

 

463B Caisa and Pylons

题意:n个格子,从0到n,每个格子有高度,第0格高度为0。初始能量为0,从i到i+1,能量变化h[i]-h[i+1]。游戏中随时可以选择一些格子增加高度,增加一单位高度需要一美刀,要求每时每刻能量都不小于0,问最小花费。

分析:题解给的是一遍循环,这次跳跃能量变化为负值时,就给当前的位置增加高度,一直保持0的能量。实际上把式子写出来,就会发现答案就是n个格子的最大高度。即h0-h1+h1-h2+h2-h3...+hi-hi+1,中间都消掉了,依据题意对于任意i,h0-hi>=0,即是h0>=max(hi)。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 int n, h;
 7 int main()
 8 {
 9     while(scanf("%d", &n) != EOF)
10     {
11         int ans = 0;
12         for (int i = 0; i < n; i++){
13             scanf("%d", &h);
14             if (h > ans) ans = h;
15         }
16         printf("%d\n", ans);
17     }
18     return 0;
19 }
View Code

 

 

463C Gargari and Bishops

题意:在n*n棋盘上放两只国际象棋的象,要求两个象不能攻击到同一个格子。每个格子有分数,获得象可以攻击到的位置的分数(包括所在位置),求最大分数和摆放位置。

分析:对棋盘黑白染色,两象不能攻击一个位置,其实就是要求他们在不同颜色的格子上(象可以在两步内到达任意相同颜色的格子,和题意等价),这样就很简单了,预处理出对角线的和,然后o(n^2)黑白两色各取最大值即可。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 
 6 typedef long long LL;
 7 LL sum1[6000], sum2[6000];
 8 int m[2100][2100];
 9 int n;
10 int main()
11 {
12     memset(sum1, 0, sizeof(sum1));
13     memset(sum2, 0, sizeof(sum2));
14     scanf("%d", &n);
15     for (int i = 0; i < n; i++)
16         for (int j = 0; j < n; j++){
17             scanf("%d", &m[i][j]);
18             sum1[i+j] = sum1[i+j] + m[i][j];
19             sum2[i-j+n-1] = sum2[i-j+n-1] + m[i][j];
20         }
21     LL ans1 = -1, ans2 = -1, tmp;
22     int x1, y1, x2, y2;
23     for (int i = 0; i < n; i++)
24         for (int j = 0; j < n; j++){
25             tmp = sum1[i+j] + sum2[i-j+n-1] - m[i][j];
26             if ((i+j)&1){
27                 if (tmp > ans1){
28                     ans1 = tmp;
29                     x1 = i+1, y1 = j+1;
30                 }
31             }
32             else{
33                 if (tmp > ans2){
34                     ans2 = tmp;
35                     x2 = i+1, y2 = j+1;
36                 }
37             }
38         }
39         cout << ans1+ans2 << endl;
40         printf("%d %d %d %d\n", x1, y1, x2, y2);
41     return 0;
42 }
View Code

 

 

463D Gargari and Permutations

题意:有k组n个数的全排列,求LCS(k <= 5, n <= 1000)。

分析:感觉这题再推广会很有意思,即多个串,元素无限制的LCS。可惜不知道怎么做=。=这题目前知道两种做法。

1.因为序列里的元素确定,n个数并且不重复,所以我们可以把他们看做n个点,如果在k个序列中i的位置都比j靠前,那么i就可以连出一条到j的边,这就获得了一个有向无环图。然后问题就转化成了求有向无环图上的最长的路。拓扑排序就可以搞定了。建图o(kn^2),拓扑排序o(n)

2.dp。dp[i]表示以i结尾的最长公共子序列的长度,则dp[i] = max(dp[j] + 1)。关键在于找到拓扑关系来转移。j能转移给i,那j必须在任何串中的位置都比i靠前,我们不妨选取第一个串来枚举,对于第i个位置的数,对于j属于0..i-1,如果第j个数在其他串中也都比第i个数靠前,那么就可以转移给i,而且dp[j]肯定是计算过的。也是o(kn^2)的。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<queue>
 5 using namespace std;
 6 
 7 struct edge{
 8     int n, v;
 9 } e[1000100];
10 int n, k, esize;
11 int en[1010], in[1010], d[1010];
12 int s[6][1010], p[1010][6];
13 void addedge(int u, int v){
14     e[esize].v = v;
15     e[esize].n = en[u];
16     en[u] = esize ++;
17 }
18 queue<int> q;
19 int main()
20 {
21     scanf("%d %d", &n, &k);
22     for (int i = 0; i < k; i++)
23         for (int j = 0; j < n; j++){
24             scanf("%d", &s[i][j]);
25             p[s[i][j]][i] = j;
26         }
27     memset(en, -1, sizeof(en));
28     memset(in, 0, sizeof(in));
29     esize = 0;
30     for (int i = 1; i <= n; i++)
31         for (int j = 1; j <= n; j++){
32             bool flag = true;
33             for (int l = 0; l < k; l++)
34                 if (p[i][l] >= p[j][l]){
35                     flag = false;
36                     break;
37                 }
38             if (flag){
39                 addedge(i, j);
40                 in[j] ++;
41             }
42         }
43     memset(d, 0, sizeof(d));
44     int ans = 0;
45     while (!q.empty()) q.pop();
46     for (int i = 1; i <= n; i++)
47         if (!in[i]) q.push(i);
48     while(!q.empty()){
49         int u = q.front(); q.pop();
50         ans = max(d[u], ans);
51         for (int t = en[u]; t != -1; t = e[t].n){
52             int v = e[t].v;
53             in[v] --;
54             d[v] = max(d[v], d[u]+1);
55             if (!in[v]) q.push(v);
56         }
57     }
58     printf("%d\n", ans+1);
59     return 0;
60 }
topsort

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 int n, k;
 7 int s[6][1010], p[1010][6];
 8 int dp[1010];
 9 int main()
10 {
11     scanf("%d %d", &n, &k);
12     for (int i = 0; i < k; i++)
13         for (int j = 0; j < n; j++){
14             scanf("%d", &s[i][j]);
15             p[s[i][j]][i] = j;
16         }
17     memset(dp, 0, sizeof(dp));
18     for (int i = 0; i < n; i++){
19         int u = s[0][i];
20         for (int j = 0; j < i; j++){
21             int v = s[0][j], l;
22             for (l = 1; l < k && p[v][l] < p[u][l]; l++);
23             if (l == k) dp[u] = max(dp[u], dp[v]+1);
24         }
25     }
26     int ans = 0;
27     for (int i = 1; i <= n; i++)
28         ans = max(ans, dp[i]);
29     printf("%d\n", ans+1);
30     return 0;
31 }
dp

 

 

463E Caisa and Tree

题意:给一棵树,每个结点有一个value(<=2*10^6),然后q次操作,一种是询问某个点u到根这条路上,是否有点v使得gcd(value(u), value(v)) > 1,输出深度最大的v的标号。另一种是修改一个结点的value(不超过50次)。

分析:comments里说这题数据水,反正我是tle了好几次=。=。因为修改不超过50次,所以我们可以在每次修改后计算好每个点的答案,然后回答询问。对于每个点,分解质因数,则到根的路上如果有祖先和自己有相同质因数,那么就满足gcd>1的要求。同时注意到value小于200万的条件,敲个代码发现200万内大概15万个质数。得到如下做法:dfs,每个素数开一个栈,存含有该质因数的结点,dfs和栈刚好契合,对于当前的点,它的质因数的栈如果不为空,那么栈顶就是离它最近也就是深度最大的符合要求的祖先,取一个深度最大的即可。然后把它push到栈里,继续dfs子节点,结束后pop掉即可。一直tle是拙计在了每次dfs到一个点就重新算一遍质因数,实际上读入时就可以分解质因数,存到vector里,当有修改操作时再单独分解那一个点,然后重新dfs。这样只有dfs的复杂度,而我重新算质因数,还要乘以算质因数的复杂度。。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<stack>
  4 #include<cmath>
  5 #include<algorithm>
  6 using namespace std;
  7 
  8 const int maxn = 2000010;
  9 struct edge{
 10     int v, n;
 11 } e[201000];
 12 int esize = 0, psize = 0;
 13 int id[maxn+5], p[maxn+5], prime[200010];
 14 int en[101000];
 15 inline void get_prime()
 16 {
 17     memset(p, 0, sizeof(p));
 18     for (int i = 2; i < maxn; i++){
 19         if (p[i] == 0){
 20             id[i] = psize;
 21             prime[psize++] = i;
 22         }
 23         for (int j = 0; j < psize && i*prime[j] < maxn && (prime[j] <= p[i] || p[i] == 0); j++)
 24             p[i*prime[j]] = prime[j];
 25     }
 26 }
 27 inline void addedge(int u, int v)
 28 {
 29     e[esize].v = v;
 30     e[esize].n = en[u];
 31     en[u] = esize ++;
 32 }
 33 int n, q, x, y, type;
 34 int v[101000], ans[101000], lv[101000];
 35 stack<int> stk[200010];
 36 inline void dfs(int x, int fa)
 37 {
 38     lv[x] = lv[fa]+1;
 39     ans[x] = -1;
 40     int len = 0, val = v[x];
 41     int pf[30];
 42     for (int i = 0; i < psize; i++){
 43         if (prime[i] * prime[i] > val) break;    //这里写成if (prime[i] > val) break; 就一直T了
 44         if (val % prime[i] == 0){
 45             pf[len++] = i;
 46             val /= prime[i];
 47             while(val % prime[i] == 0) val /= prime[i];
 48         }
 49     }
 50     if (val > 1) pf[len++] = id[val];
 51     int index;
 52     for (int i = 0; i < len; i++){
 53         index = pf[i];
 54         if (!stk[index].empty()){
 55             int tmp = stk[index].top();
 56             if (ans[x] == -1) ans[x] = tmp;
 57             else if (lv[ans[x]] < lv[tmp]) ans[x] = tmp;
 58         }
 59         stk[index].push(x);
 60     }
 61     for (int t = en[x]; t != -1; t = e[t].n){
 62         int v = e[t].v;
 63         if (v != fa) dfs(v, x);
 64     }
 65     for (int i = 0; i < len; i++){
 66         index = pf[i];
 67         stk[index].pop();
 68     }
 69 }
 70 inline int read()
 71 {
 72     int ret = 0;
 73     char ch = getchar();
 74     while(ch < '0' || ch > '9') ch = getchar();
 75     while(ch >= '0' && ch <= '9'){
 76         ret = ret * 10 + ch - '0';
 77         ch = getchar();
 78     }
 79     return ret;
 80 }
 81 int main()
 82 {
 83     get_prime();
 84     scanf("%d %d", &n, &q);
 85     for (int i = 1; i <= n; i++){
 86         v[i] = read();
 87         //scanf("%d", v+i);
 88     }
 89     memset(en, -1, sizeof(en));
 90     for (int i = 1; i < n; i++){
 91         x = read(); y = read();
 92         //scanf("%d %d", &x, &y);
 93         addedge(x, y);
 94         addedge(y, x);
 95     }
 96     lv[0] = 0;
 97     dfs(1, 0);
 98     for (int i = 0; i < q; i++){
 99         type = read();
100         //scanf("%d", &type);
101         if (type == 1){
102             //scanf("%d", &x);
103             x = read();
104             printf("%d\n", ans[x]);
105         }
106         else{
107             //scanf("%d %d", &x, &y);
108             x = read(); y = read();
109             v[x] = y;
110             dfs(1, 0);
111         }
112     }
113     return 0;
114 }
View Code

 

posted @ 2014-08-31 16:14  james47  阅读(188)  评论(0编辑  收藏  举报