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 }
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 }
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 }
题意:有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 }
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 }
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 }