题意:给你一棵树,每个点被染成了k种颜色之一或者没有颜色。你要切断恰k - 1条边使得不存在两个异色点在同一连通块内。求方案数。
解:对每颜色构建最小斯坦纳树并判交。我用的树上差分实现。
然后把同一颜色的点缩成一个点,在新树上树形DP,fx表示x子树内,x所在连通块内有一个关键点的方案数。hx表示x所在连通块内没有关键点的方案数。
1 #include <bits/stdc++.h> 2 3 const int N = 300010, MO = 998244353; 4 5 struct Edge { 6 int nex, v, len; 7 }; 8 9 int n, m, col[N], pw[N << 1], fr[N], imp[N], K, stk[N], top, f[N], h[N]; 10 std::vector<int> v[N]; 11 12 inline void ERR() { 13 puts("0"); 14 exit(0); 15 return; 16 } 17 18 struct G { 19 Edge edge[N << 1]; int tp; 20 int e[N], d[N], fa[N], pos[N], num, ST[N << 1][20]; 21 G(){} 22 inline void add(int x, int y, int z = 0) { 23 edge[++tp].v = y; 24 edge[tp].len = z; 25 edge[tp].nex = e[x]; 26 e[x] = tp; 27 return; 28 } 29 void DFS_1(int x, int f) { 30 d[x] = d[f] + 1; 31 fa[x] = f; 32 pos[x] = ++num; 33 ST[num][0] = x; 34 for(int i = e[x]; i; i = edge[i].nex) { 35 int y = edge[i].v; 36 if(y == f) continue; 37 DFS_1(y, x); 38 ST[++num][0] = x; 39 } 40 return; 41 } 42 inline void pre1(int x = 1) { 43 DFS_1(x, 0); 44 return; 45 } 46 inline void lcapre() { 47 for(int j = 1; j <= pw[num]; j++) { 48 for(int i = 1; i + (1 << j) - 1 <= num; i++) { 49 if(d[ST[i][j - 1]] < d[ST[i + (1 << (j - 1))][j - 1]]) { 50 ST[i][j] = ST[i][j - 1]; 51 } 52 else { 53 ST[i][j] = ST[i + (1 << (j - 1))][j - 1]; 54 } 55 } 56 } 57 return; 58 } 59 inline int lca(int x, int y) { 60 x = pos[x]; 61 y = pos[y]; 62 if(x > y) std::swap(x, y); 63 int t = pw[y - x + 1]; 64 if(d[ST[x][t]] < d[ST[y - (1 << t) + 1][t]]) { 65 return ST[x][t]; 66 } 67 else { 68 return ST[y - (1 << t) + 1][t]; 69 } 70 } 71 int recol(int x) { 72 int Col = 0; 73 for(int i = e[x]; i; i = edge[i].nex) { 74 int y = edge[i].v; 75 if(y == fa[x]) { 76 continue; 77 } 78 int c = recol(y); 79 if(c && Col && c != Col) { 80 ERR(); 81 } 82 if(c && !Col) { 83 Col = c; 84 } 85 } 86 if(col[x]) { 87 if(Col && col[x] != Col) { 88 ERR(); 89 } 90 else { 91 Col = col[x]; 92 } 93 } 94 col[x] = Col; 95 if(fr[x]) { 96 Col = 0; 97 } 98 return Col; 99 } 100 inline int build_t(G &gr) { 101 /*printf("build virtue tree "); 102 for(int i = 1; i <= K; i++) printf("%d ", imp[i]); 103 puts(" ");*/ 104 105 stk[top = 1] = imp[1]; 106 for(int i = 2; i <= K; i++) { 107 int x = imp[i], y = lca(x, stk[top]); 108 while(top > 1 && pos[y] <= pos[stk[top - 1]]) { 109 gr.add(stk[top - 1], stk[top], d[stk[top]] - d[stk[top - 1]]); 110 top--; 111 } 112 if(y != stk[top]) { 113 gr.add(y, stk[top], d[stk[top]] - d[y]); 114 stk[top] = y; 115 } 116 stk[++top] = x; 117 } 118 while(top > 1) { 119 gr.add(stk[top - 1], stk[top], d[stk[top]] - d[stk[top - 1]]); 120 top--; 121 } 122 return stk[top]; 123 } 124 int cal(int x) { 125 int ans = 1; 126 for(int i = e[x]; i; i = edge[i].nex) { 127 int y = edge[i].v; 128 int t = cal(y); 129 ans = 1ll * ans * t % MO * edge[i].len % MO; 130 } 131 return ans; 132 } 133 void DP(int x, int father) { 134 f[x] = col[x] ? 1 : 0; 135 h[x] = col[x] ? 0 : 1; 136 for(int i = e[x]; i; i = edge[i].nex) { 137 int y = edge[i].v; 138 if(y == father) continue; 139 DP(y, x); 140 /// 141 int t, t2; 142 t = 1ll * f[x] * (f[y] + h[y]) % MO + 1ll * f[y] * h[x] % MO; 143 t2 = 1ll * h[x] * (h[y] + f[y]) % MO; 144 f[x] = t % MO; 145 h[x] = t2; 146 } 147 //printf("x = %d f[x] = %d h[x] = %d ", x, f[x], h[x]); 148 return; 149 } 150 }g[3]; 151 152 inline bool cmp(const int &a, const int &b) { 153 return g[1].pos[a] < g[1].pos[b]; 154 } 155 156 void out(int x, G &gr) { 157 for(int i = gr.e[x]; i; i = gr.edge[i].nex) { 158 int y = gr.edge[i].v; 159 if(y == gr.fa[x]) continue; 160 //printf("%d -> %d len = %d ", x, y, gr.edge[i].len); 161 out(y, gr); 162 } 163 return; 164 } 165 166 int main() { 167 168 scanf("%d%d", &n, &m); 169 for(int i = 2; i <= n * 2; i++) pw[i] = pw[i >> 1] + 1; 170 for(int i = 1; i <= n; i++) { 171 scanf("%d", &col[i]); 172 if(col[i]) { 173 v[col[i]].push_back(i); 174 } 175 } 176 177 for(int i = 1; i < n; i++) { 178 int x, y; 179 scanf("%d%d", &x, &y); 180 g[0].add(x, y); 181 g[0].add(y, x); 182 } 183 g[0].pre1(); 184 g[0].lcapre(); 185 for(int i = 1; i <= m; i++) { 186 //std::sort(v[i].begin(), v[i].end()); 187 int len = v[i].size(), x = v[i][0]; 188 for(int j = 1; j < len; j++) { 189 x = g[0].lca(x, v[i][j]); 190 } 191 fr[x] = i; 192 } 193 194 g[0].recol(1); 195 196 /*for(int i = 1; i <= n; i++) { 197 printf("i = %d col = %d ", i, col[i]); 198 } 199 puts("");*/ 200 201 for(int x = 1; x <= n; x++) { 202 //printf("x = %d ", x); 203 for(int i = g[0].e[x]; i; i = g[0].edge[i].nex) { 204 int y = g[0].edge[i].v; 205 //printf("%d -> %d ", x, y); 206 if(!col[x] && !col[y]) { 207 g[1].add(x, y); 208 //printf("g1 : add %d %d ", x, y); 209 } 210 else if(!col[x]) { 211 g[1].add(x, v[col[y]][0]); 212 //printf("g1 : add %d %d ", x, v[col[y]][0]); 213 } 214 else if(!col[y]) { 215 g[1].add(v[col[x]][0], y); 216 //printf("g1 : add %d %d ", v[col[x]][0], y); 217 } 218 else if(col[x] != col[y]) { 219 g[1].add(v[col[x]][0], v[col[y]][0]); 220 //printf("g1 : add %d %d ", v[col[x]][0], v[col[y]][0]); 221 } 222 } 223 } 224 225 g[1].DP(v[1][0], 0); 226 227 printf("%d ", f[v[1][0]]); 228 return 0; 229 230 231 232 g[1].pre1(v[1][0]); 233 g[1].lcapre(); 234 235 /*printf("out G1 : "); 236 out(v[1][0], g[1]); 237 puts("");*/ 238 239 for(int i = 1; i <= m; i++) { 240 imp[++K] = v[i][0]; 241 } 242 /*for(int i = 1; i <= n; i++) { 243 if(!col[i]) { 244 imp[++K] = i; 245 } 246 }*/ 247 248 std::sort(imp + 1, imp + K + 1, cmp); 249 int rt = g[1].build_t(g[2]); 250 251 //printf("out G2 : "); 252 //out(rt, g[2]); 253 254 int ans = g[2].cal(rt); 255 printf("%d ", ans); 256 return 0; 257 }
代码中有一些冗余部分....
CF1144G - Two Merged Sequences
题意:给你一个序列,你要把它拆成一个单增和一个单减序列。n <= 200000, 空间256M。
解:我一开始想到了一个2-sat + 主席树优化连边的做法,但是因为过高的空间复杂度导致MLE/RE.....
正解是DP,我们可以设fi表示第i个元素在上升序列中,下降序列的结尾最大值。gi反之。有4种转移。
1 #include <bits/stdc++.h> 2 3 const int N = 200010; 4 5 int a[N], f[N], g[N], fr[N], gr[N], vis[N]; 6 7 int main() { 8 int n; 9 scanf("%d", &n); 10 for(int i = 1; i <= n; i++) { 11 scanf("%d", &a[i]); 12 } 13 memset(g, 0x3f, sizeof(g)); 14 memset(f, -1, sizeof(f)); 15 f[1] = N, g[1] = -1; 16 for(int i = 2; i <= n; i++) { 17 if(a[i - 1] < a[i] && f[i] <= f[i - 1]) { 18 f[i] = f[i - 1]; 19 fr[i] = 1; 20 } 21 if(g[i - 1] < a[i] && f[i] <= a[i - 1]) { 22 f[i] = a[i - 1]; 23 fr[i] = 2; 24 } 25 if(a[i - 1] > a[i] && g[i] >= g[i - 1]) { 26 g[i] = g[i - 1]; 27 gr[i] = 2; 28 } 29 if(f[i - 1] > a[i] && g[i] >= a[i - 1]) { 30 g[i] = a[i - 1]; 31 gr[i] = 1; 32 } 33 //printf("i = %d f = %d g = %d ", i, f[i], g[i]); 34 } 35 36 if(f[n] != -1) { 37 printf("YES "); 38 int t = 1; 39 for(int i = n; i >= 1; i--) { 40 vis[i] = t; 41 if(t == 1) t = fr[i]; 42 else t = gr[i]; 43 } 44 for(int i = 1; i <= n; i++) { 45 printf("%d ", vis[i] - 1); 46 } 47 return 0; 48 } 49 if(g[n] != 0x3f3f3f3f) { 50 printf("YES "); 51 int t = 2; 52 for(int i = n; i >= 1; i--) { 53 vis[i] = t; 54 if(t == 1) t = fr[i]; 55 else t = gr[i]; 56 } 57 for(int i = 1; i <= n; i++) { 58 printf("%d ", vis[i] - 1); 59 } 60 return 0; 61 } 62 printf("NO "); 63 return 0; 64 }
1 #include <bits/stdc++.h> 2 3 const int N = 6000010, lm = 200001; 4 5 struct Edge { 6 int nex, v; 7 }edge[N]; int tp; 8 9 int n, tot, e[N], ls[N], rs[N], dfn[N], low[N], num, a[200010], rt[200010], rt2[200010], rt3[200010], rt4[200010]; 10 int fr[N], scc_cnt; 11 std::stack<int> S; 12 13 inline void add(int x, int y) { 14 edge[++tp].v = y; 15 edge[tp].nex = e[x]; 16 e[x] = tp; 17 return; 18 } 19 20 void tarjan(int x) { 21 low[x] = dfn[x] = ++num; 22 S.push(x); 23 for(int i = e[x]; i; i = edge[i].nex) { 24 int y = edge[i].v; 25 if(!dfn[y]) { 26 tarjan(y); 27 low[x] = std::min(low[x], low[y]); 28 } 29 else if(!fr[y]) { 30 low[x] = std::min(low[x], dfn[y]); 31 } 32 } 33 if(low[x] == dfn[x]) { 34 int y; 35 ++scc_cnt; 36 do { 37 y = S.top(); 38 S.pop(); 39 fr[y] = scc_cnt; 40 } while(x != y); 41 } 42 return; 43 } 44 45 void insert(int &x, int y, int p, int id, int l, int r) { 46 if(!x || x == y) { 47 x = ++tot; 48 ls[x] = ls[y]; 49 rs[x] = rs[y]; 50 } 51 if(l == r) { 52 add(x, id); 53 if(y) add(x, y); 54 return; 55 } 56 int mid = (l + r) >> 1; 57 if(p <= mid) { 58 insert(ls[x], ls[y], p, id, l, mid); 59 } 60 else { 61 insert(rs[x], rs[y], p, id, mid + 1, r); 62 } 63 if(ls[x]) { 64 add(x, ls[x]); 65 } 66 if(rs[x]) { 67 add(x, rs[x]); 68 } 69 return; 70 } 71 72 void Add(int id, int L, int R, int l, int r, int o) { 73 if(!o) return; 74 if(L <= l && r <= R) { 75 add(id, o); 76 return; 77 } 78 int mid = (l + r) >> 1; 79 if(L <= mid) { 80 Add(id, L, R, l, mid, ls[o]); 81 } 82 if(mid < R) { 83 Add(id, L, R, mid + 1, r, rs[o]); 84 } 85 return; 86 } 87 88 int main() { 89 printf("%d ", (sizeof(e) * 8 + sizeof(a) * 5) / 1048576); 90 scanf("%d", &n); 91 for(int i = 1; i <= n; i++) { 92 scanf("%d", &a[i]); 93 a[i]++; 94 } 95 96 /// build 97 tot = n * 2; 98 for(int i = 1; i <= n; i++) { 99 if(i < n) { 100 insert(rt[i], rt[i - 1], a[i], i + n, 1, lm); 101 } 102 if(i > 1) { 103 Add(i, a[i], lm, 1, lm, rt[i - 1]); 104 } 105 } 106 107 for(int i = 1; i <= n; i++) { 108 if(i < n) { 109 insert(rt2[i], rt2[i - 1], a[i], i, 1, lm); 110 } 111 if(i > 1) { 112 Add(i + n, 1, a[i], 1, lm, rt2[i - 1]); 113 } 114 } 115 116 for(int i = n; i >= 1; i--) { 117 if(i > 1) { 118 insert(rt3[i], rt3[i + 1], a[i], i + n, 1, lm); 119 } 120 if(i < n) { 121 Add(i, 1, a[i], 1, lm, rt3[i + 1]); 122 } 123 } 124 125 for(int i = n; i >= 1; i--) { 126 if(i > 1) { 127 insert(rt4[i], rt4[i + 1], a[i], i, 1, lm); 128 } 129 if(i < n) { 130 Add(i + n, a[i], lm, 1, lm, rt4[i + 1]); 131 } 132 } 133 134 for(int i = 1; i <= tot; i++) { 135 if(!dfn[i]) { 136 tarjan(i); 137 } 138 } 139 140 for(int i = 1; i <= n; i++) { 141 if(fr[i] == fr[i + n]) { 142 puts("NO"); 143 return 0; 144 } 145 } 146 puts("YES"); 147 for(int i = 1; i <= n; i++) { 148 if(fr[i] < fr[i + n]) { 149 printf("0 "); 150 } 151 else { 152 printf("1 "); 153 } 154 } 155 puts(""); 156 return 0; 157 }
CF1132E Knapsack
题意:给定8e16个物体,每个物体的体积在1 ~ 8中。求不超过W体积的情况下最接近W能得到多少体积。
解:考虑到答案要么是sum,要么不会小于W - 7,,而且由于物品体积都非常小所以随便调整几下就能得到解。
于是先贪心到W - 8以上,然后用bitset做正反两个背包,然后枚举答案判定即可。
1 #include <bits/stdc++.h> 2 3 typedef long long LL; 4 5 const int N = 10010; 6 7 std::bitset<N> f, g, h; 8 LL W, a[10], b[10]; 9 10 int main() { 11 LL sum = 0; 12 scanf("%lld", &W); 13 for(int i = 1; i <= 8; i++) { 14 scanf("%lld", &a[i]); 15 sum += a[i] * i; 16 } 17 if(sum <= W) { 18 printf("%lld ", sum); 19 return 0; 20 } 21 22 sum = 0; 23 for(int i = 1; i <= 8; i++) { 24 if(sum + a[i] * i <= W) { 25 sum += a[i] * i; 26 std::swap(a[i], b[i]); 27 } 28 else { 29 LL cnt = (W - sum) / i; 30 sum += cnt * i; 31 b[i] = cnt; 32 a[i] -= cnt; 33 break; 34 } 35 } 36 37 f.set(0); 38 g.set(0); 39 for(int i = 1; i <= 8; i++) { 40 int lm = std::min(a[i], 100ll); 41 for(int j = 1; j <= lm; j++) { 42 f |= f << i; 43 } 44 } 45 for(int i = 1; i <= 8; i++) { 46 int lm = std::min(b[i], 100ll); 47 for(int j = 1; j <= lm; j++) { 48 g |= g << i; 49 } 50 } 51 52 int delta = W - sum; 53 for(int i = delta; i >= 0; i--) { 54 /// check if f-g = i 55 h = (g << i) & f; 56 if(h.any()) { 57 printf("%lld ", i + sum); 58 return 0; 59 } 60 } 61 62 return 0; 63 }
CF