【训练】9.26~10.2 训练赛合集
9.26 H.HDU 6562
线段树操作特点:1.能统计出一个操作对一个区间整体的影响;2.能通过标记表示出该操作对左右子区间的影响;3.标记的含义为子区间应当进行的操作,标记应当能够合并(具有先后顺序)。此题中我们维护区间\(10^{a}\) 的和,则区间的值的增加量为 \(d * 10^{a}\),通过乘法与加法标记进行记录。注意标记之间的先后运算顺序。
#include <bits/stdc++.h> using namespace std; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } void P_D_1(int p, int l, int r) { int ls = p << 1, rs = p << 1 | 1, mid = (l + r) >> 1; if(mul1[p] != 1) { mul1[ls] = mul(mul1[ls], mul1[p]); mul1[rs] = mul(mul1[rs], mul1[p]); plus1[ls] = mul(plus1[ls], mul1[p]); plus1[rs] = mul(plus1[rs], mul1[p]); num1[ls] = mul(num1[ls], mul1[p]); num1[rs] = mul(num1[rs], mul1[p]); mul1[p] = 1; } if(plus1[p]) { plus1[ls] = add(plus1[ls], plus1[p]); plus1[rs] = add(plus1[rs], plus1[p]); num1[ls] = add(num1[ls], mul(mid - l + 1, plus1[p])); num1[rs] = add(num1[rs], mul(r - mid, plus1[p])); plus1[p] = 0; } } void Mod1_1(int p, int l, int r, int L, int R, int x) { if(l > R || r < L) return; if(l >= L && r <= R) { plus1[p] = add(plus1[p], x); num1[p] = add(num1[p], mul(r - l + 1, x)); return; } P_D_1(p, l, r); int mid = (l + r) >> 1; Mod1_1(p << 1, l, mid, L, R, x); Mod1_1(p << 1 | 1, mid + 1, r, L, R, x); } void Mod1_2(int p, int l, int r, int L, int R, int x) { if(l > R || r < L) return; if(l >= L && r <= R) { plus1[p] = mul(plus1[p], x); mul1[p] = mul(mul1[p], x); num1[p] = mul(num1[p], x); return; } P_D_1(p, l, r); int mid = (l + r) >> 1; Mod1_1(p << 1, l, mid, L, R, x); Mod1_1(p << 1 | 1, mid + 1, r, L, R, x); } void P_D_2(int p, int l, int r) { int ls = p << 1, rs = p << 1 | 1, mid = (l + r) >> 1; if(mul3[p] != 1) { mul3[ls] = mul(mul3[ls], mul3[p]); mul3[rs] = mul(mul3[rs], mul3[p]); plus2[ls] = mul(mul3[p], plus2[ls]); plus2[rs] = mul(mul3[p], plus2[rs]); num2[ls] = mul(mul3[p], num2[ls]); num2[rs] = mul(mul3[p], num2[rs]); mul3[p] = 1; } if(mul2[p] != 1) { } } void Mod2_3(int p, int l, int r, int L, int R, int x) { if(l > R || r < L) return; if(l >= L && r <= R) { plus2[p] = mul(plus2[p], x); mul3[p] = mul(mul3[p], x); num2[p] = mul(num2[p], x); return; } int mid = (l + r) >> 1; P_D_2(p, l, r); Mod2_3(p << 1, l, mid, L, R, x); Mod2_3(p << 1 | 1, mid + 1, r, L, R, x); } void Mod2_1(int p, int l, int r, int L, int R, int x) { if(l > R || r < L) return; if(l >= L && r <= R) { plus2[p] = add(plus2[p], x); num2[p] = add(num2[p], mul(x, sum2[p])); return; } int mid = (l + r) >> 1; P_D_2(p, l, r); Mod2_1(p << 1, l, mid, L, R, x); Mod2_1(p << 1 | 1, mid + 1, r, L, R, x); } void Mod2_2(int p, int l, int r, int L, int R, int x) { if(l > R || r < L) return; if(l >= L && r <= R) { plus2[p] = mul(plus2[p], inv100); mul2[p] = mul(mul2[p], x); num2[p] = mul(num2[p], x); return; } int mid = (l + r) >> 1; P_D_2(p, l, r); Mod2_2(p << 1, l, mid, L, R, x); Mod2_2(p << 1 | 1, mid + 1, r, L, R, x); } int main() { int T = read(); for(int t = 1; t <= T; t ++) { n = read(), m = read(); for(int i = 1; i <= m; i ++) { scanf("%s", opt + 1); if(opt[1] == 'w') { int l = read(), r = read(), d = read(); Mod1_2(1, 1, n, l, r, 10); Mod1_1(1, 1, n, l, r, d); Mod2_3(1, 1, n, l, r, 10); Mod2_1(1, 1, n, l, r, mul(d, 10)); Mod2_2(1, 1, n, l, r, 100); } else { int l = read(), r = read(); Que(1, 1, n, l, r); } } } return 0; }
10.1 A.Gym 102394A
所给出的两个限制在二分答案之后变为若干区间中点的个数具有上限及下限,通过差分约束跑最长路得到最小解。(注意此处二分答案mid后的判定:隐藏条件为选出了恰好 mid 个点,所以应当保证 sum[n] = mid, 改写为 sum[n] - sum[0] >= mid, sum[n] - sum[0] <= mid)。
#include <bits/stdc++.h> using namespace std; #define maxn 120000 #define INF 99999999 int n, m1, m2, reccnp, cnt[maxn], mark[maxn], cap[maxn], dis[maxn]; deque <int> q; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') { x = x * 10 + c - '0', c = getchar(); } return x * k; } struct edge { int cnp = 1, head[maxn], last[maxn * 3], w[maxn * 3], to[maxn * 3]; void add(int u, int v, int co) { last[cnp] = head[u], to[cnp] = v, w[cnp] = co, head[u] = cnp ++; } }E; struct node { int l, r, num; }a[maxn], b[maxn]; int Spfa() { int S = n + 2; q.push_front(S); for(int i = 1; i <= n + 1; i ++) dis[i] = -INF, mark[i] = cnt[i] = 0; dis[n + 2] = mark[n + 2] = cnt[n + 2] = 0; while(!q.empty()) { int u = q.front(); q.pop_front(); mark[u] = 0; for(int i = E.head[u]; i; i = E.last[i]) { int v = E.to[i]; if(dis[v] < dis[u] + E.w[i]) { dis[v] = dis[u] + E.w[i]; if(!mark[v]) { mark[v] = 1, cnt[v] ++; if(!q.empty() && dis[v] > dis[q.front()]) q.push_front(v); else q.push_back(v); if(cnt[v] > (n + 2)) return -1; } } } } if(dis[n + 1] < 0 || dis[n + 2] < 0) return -1; return dis[n]; } bool Check(int mid) { E.cnp = reccnp; for(int i = 1; i <= n + 2; i ++) E.head[i] = cap[i]; E.add(n, n + 1, -mid), E.add(n + 1, n, mid); for(int i = 1; i <= m2; i ++) if(b[i].l != 1) E.add(b[i].r, b[i].l - 1, b[i].num - mid); else E.add(b[i].r, n + 1, b[i].num - mid); int t = Spfa(); if(t == -1) return 0; else return 1; } signed main() { int T = read(); while(T --) { E.cnp = 1; for(int i = 1; i <= n + 2; i ++) E.head[i] = 0; n = read(), m1 = read(), m2 = read(); E.add(n + 2, n + 1, 0); E.add(n + 1, n + 2, -1); for(int i = 1; i < n; i ++) E.add(i, i + 1, 0), E.add(i + 1, i, -1); E.add(n + 1, 1, 0), E.add(1, n + 1, -1); for(int i = 1; i <= m1; i ++) { a[i].l = read(), a[i].r = read(), a[i].num = read(); if(a[i].l != 1) E.add(a[i].l - 1, a[i].r, a[i].num); else E.add(n + 1, a[i].r, a[i].num); } reccnp = E.cnp; for(int i = 1; i <= n + 2; i ++) cap[i] = E.head[i]; for(int i = 1; i <= m2; i ++) b[i].l = read(), b[i].r = read(), b[i].num = read(); int l = 1, r = n; while(l < r) { int mid = (l + r) >> 1; if(Check(mid)) r = mid; else l = mid + 1; } if(l == 1 && Check(0)) printf("0\n"); else printf("%d\n", l); } return 0; }
10.2
E.Gym - 102361E:一个格子要么转换方向要么不转,若转换方向则只有一个机器人经过,若不转换方向则最多两个,一个走上下方向,一个走左右方向。图上面求一个高度自动化的方案联想网络流建图,跑最大流判断是否均可到达终点。
#include <bits/stdc++.h> using namespace std; #define maxn 2000000 #define INF 999999 #define N 200 int n, m, a, b, S, T, tot, cur[maxn], lev[maxn]; char s[N][N]; queue <int> q; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } struct edge { int cnp = 2, head[maxn], last[maxn], to[maxn], c[maxn], f[maxn]; void add(int u, int v, int cap) { to[cnp] = v, c[cnp] = cap, last[cnp] = head[u], head[u] = cnp ++; to[cnp] = u, c[cnp] = 0, last[cnp] = head[v], head[v] = cnp ++; } }E; int id(int x, int y, int opt) { return (x - 1) * m + y + (opt == 2) * n * m; } int bfs() { for(int i = 1; i <= tot; i ++) lev[i] = -1; while(!q.empty()) q.pop(); q.push(S); lev[S] = 1; while(!q.empty()) { int u = q.front(); q.pop(); for(int i = E.head[u]; i; i = E.last[i]) { int v = E.to[i]; if((lev[v] == -1) && (E.c[i] > 0)) { lev[v] = lev[u] + 1; q.push(v); } if(lev[T] != -1) return 1; } } return 0; } int dfs(int u, int f) { if(f <= 0) return 0; if(u == T) return f; int ans = 0; for(int i = cur[u]; i; i = E.last[i]) { int v = E.to[i]; if(!f) break; if((lev[v] == lev[u] + 1) && E.c[i]) { cur[u] = i; int tf = dfs(v, min(f, E.c[i])); f -= tf, ans += tf, E.c[i] -= tf, E.c[i ^ 1] += tf; } } return ans; } int Dinic() { int ans = 0; while(bfs()) { for(int i = 1; i <= tot; i ++) cur[i] = E.head[i]; ans += dfs(S, INF); } return ans; } int main() { int T1 = read(); while(T1 --) { n = read(), m = read(), a = read(), b = read(), tot = n * m; for(int i = 1; i <= n; i ++) scanf("%s", s[i] + 1); for(int i = 0; i <= m + 1; i ++) s[0][i] = '1', s[n + 1][i] = '1'; for(int i = 1; i <= n; i ++) s[i][0] = '1', s[i][m + 1] = '1'; E.cnp = 2; for(int i = 1; i <= tot * 2 + 2; i ++) E.head[i] = 0; for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) { if(s[i][j] == '1') continue; int x1 = i - 1, y1 = j; if(s[x1][y1] != '1') { E.add(id(i, j, 1), id(x1, y1, 1), 1); E.add(id(x1, y1, 1), id(i, j, 1), 1); } x1 = i, y1 = j - 1; if(s[x1][y1] != '1') { E.add(id(i, j, 2), id(x1, y1, 2), 1); E.add(id(x1, y1, 2), id(i, j, 2), 1); } E.add(id(i, j, 1), id(i, j, 2), 1); E.add(id(i, j, 2), id(i, j, 1), 1); } tot = n * m * 2 + 2; S = tot - 1, T = tot; for(int i = 1; i <= a; i ++) { int p = read(); if(s[1][p] == '1') continue; E.add(S, id(1, p, 1), 1); } for(int i = 1; i <= b; i ++) { int p = read(); if(s[n][p] == '1') continue; E.add(id(n, p, 1), T, 1); } if(Dinic() == a) printf("Yes\n"); else printf("No\n"); } return 0; }
K.Gym - 102361K
分析,如果一个叶子节点到父亲的边为一个分支,则拿掉该叶子节点之后后手所能进行的操作先手也均可进行。因此若拿掉该叶子节点之后为先手必胜,则先手继续把后手会拿走的节点拿走使局面转化为先手必败即可。由此可知只要存在有长度为1的叶子分支则先手必胜。若所有的分支长度均为2,则先手必败。由此我们将所有的分支的长度计算出来并-2,问题转化为有若干堆石子每次可以从任意个堆中每堆取走一个,最后无法操作的人失败。则若有奇数堆先手必胜,否则先手必败。对于树退化成链的情况进行特判。
#include <bits/stdc++.h> using namespace std; #define maxn 1200000 int rec[maxn], fa[maxn]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } int main() { int T = read(); while(T --) { int n = read(); bool flag = 0; for(int i = 1; i <= n; i ++) rec[i] = 0; for(int i = 2; i <= n; i ++) { int x = read(); rec[x] ++; fa[i] = x; } for(int i = 1; i <= n; i ++) if(!rec[i]) { int j = i, num = 0; while(fa[j] && rec[j] <= 1) j = fa[j], num ++; if(rec[j] <= 1) { flag = 1; if(n & 1) printf("Takeru\n"); else printf("Meiya\n"); } else if(num & 1) { flag = 1; printf("Takeru\n"); break; } } if(!flag) printf("Meiya\n"); } return 0; }