ZROI 2019 寒假省选线下自闭赛
预计得分:$20+10+60=90$
实际得分:$20+10+20=50$
自闭了,,,T3想出K=1的做法然后写炸,还要比暴力低10分
不过这次考试倒是给了我一个教训,就是在考虑复杂度的时候千万不能感性理解
多花点时间去算一下具体的复杂度,不会算也要构造数据跑一下
不要动不动就觉得这个东西是$O(nlogn)$的,有时候一不注意就变成了$O(n^2)$
$T1:$
20分直接模拟即可
对于这种题,一般有一个套路就是枚举长度(就是j-i+1),然后去统计答案
考虑优化统计答案的速度(就是不要每次都去找具体的i,j,k,然后再ans++)
假如有一个串:abaada,如果我们已经知道了ab和aa可以拼接,现在我们又知道了aa和da可以拼接
那显然ab,aa,da三个串也可以拼接,于是ans+=2
考虑怎么快速去发现一段子串s[i...j]是否能与s[i+len...j+len]能够拼接(即至多只有一个位置不同)
我们可以$O(n)$的处理出一个数组S,S[i]表示s[i]是否与s[i+len]不相同
如果某段S[i..j]之和小于等于1的话,就说明这段能够和s[i+len...j+len]相拼接(利用前缀和即可快速知道区间和)
然后数组dp[i]则表示s[i-len+1...i]能和几段拼接,于是我们可以就可以在$O(\frac{n}{2}*n)$的时间内求出答案
对于那20分的随机部分,只需将枚举的长度限制得比较小即可,比如50或100
满分算法先咕一下,搞懂了再说
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 char str[100010]; 5 int n, maxx, s[100010], dp[100010]; 6 long long ans; 7 8 int main() 9 { 10 scanf("%s", str + 1); 11 n = strlen(str + 1); 12 maxx = (n <= 5000 ? n >> 1 : 100); 13 for(int len = 2; len <= maxx; ++len) 14 { 15 memset(s, 0, sizeof(s)); 16 memset(dp, 0, sizeof(dp)); 17 for(int i = 1; i <= n - len; ++i) s[i] = (str[i] != str[i + len]); 18 for(int i = 1; i <= n - len; ++i) s[i] += s[i - 1]; 19 for(int i = len; i <= n - len; ++i) 20 if(s[i] - s[i - len] <= 1) 21 dp[i] = dp[i - len] + 1, ans += dp[i]; 22 } 23 printf("%lld\n", ans); 24 return 0; 25 }
$T2:$
期望???多项式???
反正都不会,写下十分暴力走人
会了再来写(咕咕)
$T3:$
看着题目里的最小生成树,我突然想到了当年在XJOI上考过的那个题,就是很多个点的完全图(1e5)求最小生成树(虽然当时并不会还是机房dalao教我的)
k=1的情况差不多也是同理,我们考虑做Kruskal的过程,是要从小到大的枚举边,然后连满m-1条边(因为line graph是m个点)
根据题意,line graph里的边就是原图中的公共点,于是我们将原图的点排个序从小到大枚举
如果当前这个点的出度超过1,说明在line graph中肯定有一条边的权值是这个点的点权,于是我们就把这条边所代表的两个集合合并即可
用并查集维护,复杂度大概是$O(n+m)$加并查集复杂度(可能不是?反正我不怎么会算,能过就对了)
k=2的情况可以先暴力做一遍,然后按照k=1的情况处理
这样就有60分的好成绩了,什么你说100分??
先咕着把,会了再说
代码(可能比较长,因为在考场上改来改去的):
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 namespace solve_bf 5 { 6 struct Edge 7 { 8 int from, to, val; 9 bool operator < (const Edge &x) const 10 { 11 return val < x.val; 12 }; 13 }e[2000010]; 14 int point[2000010]; 15 16 int n, m, k, fa[2000010]; 17 18 int Get(int x) 19 { 20 if(x == fa[x]) return x; 21 return fa[x] = Get(fa[x]); 22 } 23 24 long long Kruskal() 25 { 26 long long ans = 0; 27 for(int i = 1; i <= n; ++i) fa[i] = i; 28 sort(e + 1, e + 1 + m); 29 for(int i = 1; i <= m; ++i) 30 { 31 int f1 = Get(e[i].from); 32 int f2 = Get(e[i].to); 33 if(f1 == f2) continue; 34 fa[f1] = f2; 35 ans += e[i].val; 36 } 37 return ans; 38 } 39 40 void solve(int a, int b, int c) 41 { 42 n = a, m = b, k = c; 43 for(int i = 1; i <= n; ++i) scanf("%d", &point[i]); 44 for(int i = 1; i <= m; ++i) scanf ("%d %d %d", &e[i].from, &e[i].to, &e[i].val); 45 printf("%lld\n", Kruskal()); 46 } 47 } 48 49 namespace solve_one 50 { 51 struct Point 52 { 53 int id, val; 54 bool operator < (const Point &x) const 55 { 56 return val < x.val; 57 }; 58 }p[2000010]; 59 60 vector<int> G[2000010]; 61 int n, m, k, cnt, fa[2000010]; 62 long long ans; 63 64 int Get(int x) 65 { 66 if(x == fa[x]) return x; 67 return fa[x] = Get(fa[x]); 68 } 69 70 void solve(int a, int b, int c) 71 { 72 n = a, m = b, k = c; 73 for(int i = 1; i <= n; ++i) 74 { 75 p[i].id = i; 76 scanf("%d", &p[i].val); 77 } 78 for(int i = 1; i <= m; ++i) 79 { 80 int u, v, z; 81 scanf("%d %d %d", &u, &v, &z); 82 G[u].push_back(i); 83 G[v].push_back(i); 84 } 85 for(int i = 1; i <= m; ++i) fa[i] = i; 86 sort(p + 1, p + 1 + n); 87 for(int i = 1; i <= n; ++i) 88 if(G[p[i].id].size() > 1) 89 { 90 int id = p[i].id; 91 for(int j = 0; j < G[id].size() - 1; ++j) 92 { 93 int f1 = Get(G[id][j]); 94 int f2 = Get(G[id][j + 1]); 95 if(f1 == f2) continue; 96 fa[f1] = f2; 97 ans += p[i].val; 98 if(++cnt == m - 1) break; 99 if(cnt == m - 1) break; 100 } 101 if(cnt == m - 1) break; 102 } 103 printf("%lld\n", ans); 104 } 105 } 106 107 namespace solve_two 108 { 109 struct Edge 110 { 111 int from, to, val; 112 bool operator < (const Edge &x) const 113 { 114 return val < x.val; 115 }; 116 }e[2000010], e2[2000010]; 117 int point[2000010], point2[2000010]; 118 119 int n, m, k, cnt, fa[2000010]; 120 long long ans; 121 122 int Get(int x) 123 { 124 if(x == fa[x]) return x; 125 return fa[x] = Get(fa[x]); 126 } 127 128 int Check(int a, int b, int c, int d) 129 { 130 if(a == b || a == c || a == d) return a; 131 if(b == c || b == d) return b; 132 if(c == d) return c; 133 return -1; 134 } 135 136 void solves() 137 { 138 int new_n = m, new_m = 0; 139 for(int i = 1; i <= new_n; ++i) point2[i] = e[i].val; 140 for(int i = 1; i <= new_n; ++i) 141 for(int j = i + 1; j <= new_n; ++j) 142 { 143 int id = Check(e[i].from, e[i].to, e[j].from, e[j].to); 144 if(id == -1) continue; 145 e2[++new_m] = (Edge){i, j, point[id]}; 146 } 147 n = new_n, m = new_m; 148 } 149 150 struct Point 151 { 152 int id, val; 153 bool operator < (const Point &x) const 154 { 155 return val < x.val; 156 }; 157 }p[2000010]; 158 vector<int> G[2000010]; 159 160 void solve(int a, int b, int c) 161 { 162 n = a, m = b, k = c; 163 for(int i = 1; i <= n; ++i) scanf("%d", &point[i]); 164 for(int i = 1; i <= m; ++i) scanf ("%d %d %d", &e[i].from, &e[i].to, &e[i].val); 165 solves(); 166 for(int i = 1; i <= n; ++i) 167 { 168 p[i].id = i; 169 p[i].val = point2[i]; 170 } 171 for(int i = 1; i <= m; ++i) 172 { 173 G[e2[i].from].push_back(i); 174 G[e2[i].to].push_back(i); 175 } 176 for(int i = 1; i <= m; ++i) fa[i] = i; 177 sort(p + 1, p + 1 + n); 178 for(int i = 1; i <= n; ++i) 179 if(G[p[i].id].size() > 1) 180 { 181 int id = p[i].id; 182 for(int j = 0; j < G[id].size() - 1; ++j) 183 { 184 int f1 = Get(G[id][j]); 185 int f2 = Get(G[id][j + 1]); 186 if(f1 == f2) continue; 187 fa[f1] = f2; 188 ans += p[i].val; 189 if(++cnt == m - 1) break; 190 if(cnt == m - 1) break; 191 } 192 if(cnt == m - 1) break; 193 } 194 printf("%lld\n", ans); 195 } 196 } 197 198 int main() 199 { 200 int n, m, k; 201 scanf("%d %d %d", &n, &m, &k); 202 if(k == 1) solve_one::solve(n, m, k); 203 else if(k == 2) solve_two::solve(n, m, k); 204 else solve_bf::solve(n, m, k); 205 return 0; 206 }