2024.06 别急记录
1. Ynoi2009 - rprsvq
首先有方差
还有结论:对于大小为
那么一个序列
即
右半边使用线段树维护,左半边
点击查看代码
//P6108 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 5e6 + 10; const ll P = 998244353; int n, m; ll pw2[N], fg[N], fac[N], inv[N], vg[N]; struct node{ int sum, len, pw, tag; } t[N*4]; ll qp(ll x, ll y){ ll ans = 1; while(y){ if(y & 1){ ans = ans * x % P; } x = x * x % P; y >>= 1; } return ans; } void build(int p, int l, int r){ t[p].len = r - l + 1; if(l != r){ int mid = l + r >> 1; build(p<<1, l, mid); build(p<<1|1, mid+1, r); } } void psd(int p, ll v){ t[p].tag = ((ll)t[p].tag + v) % P; t[p].pw = ((ll)t[p].pw + 2ll * t[p].sum * v) % P; t[p].pw = ((ll)t[p].pw + v * v % P * t[p].len) % P; t[p].sum = ((ll)t[p].sum + t[p].len * v) % P; } void add(int p, int l, int r, int ql, int qr, ll v){ if(qr < l || r < ql){ return; } else if(ql <= l && r <= qr){ psd(p, v); } else { int mid = l + r >> 1; psd(p<<1, t[p].tag); psd(p<<1|1, t[p].tag); t[p].tag = 0; add(p<<1, l, mid, ql, qr, v); add(p<<1|1, mid+1, r, ql, qr, v); t[p].sum = ((ll)t[p<<1].sum + t[p<<1|1].sum) % P; t[p].pw = ((ll)t[p<<1].pw + t[p<<1|1].pw) % P; } } pair<ll, ll> qry(int p, int l, int r, int ql, int qr){ if(qr < l || r < ql){ return make_pair(0, 0); } else if(ql <= l && r <= qr){ return make_pair(t[p].sum, t[p].pw); } else { int mid = l + r >> 1; psd(p<<1, t[p].tag); psd(p<<1|1, t[p].tag); t[p].tag = 0; pair<ll, ll> x = qry(p<<1, l, mid, ql, qr); pair<ll, ll> y = qry(p<<1|1, mid+1, r, ql, qr); x.first = (x.first + y.first) % P; x.second = (x.second + y.second) % P; return x; } } void solve(){ scanf("%d%d", &n, &m); build(1, 1, n); pw2[0] = fac[0] = 1; fg[1] = 1, fg[2] = 5, fg[3] = 29, fg[4] = 206; for(int i = 1; i <= n; ++ i){ pw2[i] = pw2[i-1] * 2 % P; fac[i] = fac[i-1] * i % P; fg[i+4] = 2ll * (i+3) * (i+2) % P * (i+2) % P * fg[i+1] % P; fg[i+4] += (P-1) * (i+3) % P * (13+i*5) % P * fg[i+2] % P; fg[i+4] += (13+i*4) * fg[i+3] % P; fg[i+4] %= P; vg[i] = qp((ll)i * (i-1) % P, P-2); } inv[n] = qp(fac[n], P-2); for(int i = n-1; i >= 0; -- i){ inv[i] = inv[i+1] * (i+1) % P; } for(int i = 1; i <= m; ++ i){ int op, l, r; scanf("%d%d%d", &op, &l, &r); if(op == 1){ ll a; scanf("%lld", &a); add(1, 1, n, l, r, a); } else { auto tmp = qry(1, 1, n, l, r); ll len = r - l + 1; ll vx = len * tmp.second % P; vx = (vx - tmp.first * tmp.first % P + P) % P; ll vy = (pw2[len] - 1 - inv[len] * fg[len] % P + P + P) % P * vg[len] % P; printf("%lld\n", vx * vy % P); } } }
2. CEOI2004 - Sweets
使用生成函数。
单个罐子
合起来
左边的部分由牛顿二项式定理
右边部分 dfs 枚举次数
即
由于
总复杂度
点击查看代码
//P6078 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } #define int long long const int P = 2004, N = 1e7 + 10; int n, a, b, p[13], ans; int cl(int x){ if(x < 0){ return 0; } int tmp[13]; for(int i = 1; i <= n; ++ i){ tmp[i] = x + i; } for(int i = 1; i <= n; ++ i){ int nw = i; for(int j = 1; j <= n; ++ j){ int p = __gcd(nw, tmp[j]); tmp[j] /= p; nw /= p; } } int ans = 1; for(int i = 1; i <= n; ++ i){ ans = ans * tmp[i] % P; } return ans; } void dfs(int x, int sum, int pl){ if(x == n + 1){ ans += pl * (cl(b-sum) - cl(a-sum-1) + P) % P; ans %= P; } else { dfs(x+1, sum, pl); dfs(x+1, sum+p[x]+1, P-pl); } } void solve(){ scanf("%d%d%d", &n, &a, &b); for(int i = 1; i <= n; ++ i){ scanf("%d", &p[i]); } dfs(1, 0, 1); printf("%d\n", ans); }
3. APC001F - XOR Tree
考虑每个节点赋权值为相邻边异或和。那么每次改链操作相当于选择两个点点权异或某个数。目标变为所有点点权均为
若两个点权相同肯定是这两个点操作最优。于是最后只剩下
点击查看代码
//AT_apc001_f #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 1e5 + 10; int n, v[N], f[1<<15], c[20]; int dfs(int x){ if(f[x] != -1){ return f[x]; } f[x] = 0x3f3f3f3f; for(int i = 1; i <= 15; ++ i){ for(int j = 1; j <= 15; ++ j){ if(!(x & (1<<i-1)) || !(x & (1<<j-1)) || i == j){ continue; } for(int k = 1; k <= 15; ++ k){ int p = i ^ k, q = j ^ k, ad = 1; if((x<<1) & (1<<p)){ ++ ad; } if((x<<1) & (1<<q)){ ++ ad; } f[x] = min(f[x], ad + dfs(((x<<1)^(1<<i)^(1<<j)^(1<<p)^(1<<q))>>1)); } } } return f[x]; } void solve(){ scanf("%d", &n); for(int i = 1; i < n; ++ i){ int x, y, a; scanf("%d%d%d", &x, &y, &a); v[x] ^= a; v[y] ^= a; } for(int i = 0; i < n; ++ i){ ++ c[v[i]]; } int st = 0, ans = 0; for(int i = 1; i <= 15; ++ i){ if(c[i] & 1){ st |= (1 << i - 1); } ans += c[i] >> 1; } memset(f, -1, sizeof(f)); f[0] = 0; printf("%d\n", ans + dfs(st)); }
4. APC001E - Antennas on Tree
显然一个合法解的充要条件是对于任意一个节点,它的子树中至多有一棵中没有标记点,那么标记叶子显然是更优的。
考虑一个不存在度数
点击查看代码
//AT_apc001_e #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 1e5 + 10; int n, f[N][3], mx; vector<int> g[N], h[N]; void dfs(int x, int fa, int tp){ if(g[x].size() != 2){ h[tp].push_back(x); } else { for(int i : g[x]){ if(i != fa){ dfs(i, x, tp); } } } } void solve(){ scanf("%d", &n); for(int i = 1; i < n; ++ i){ int a, b; scanf("%d%d", &a, &b); ++ a; ++ b; g[a].push_back(b); g[b].push_back(a); mx = max(mx, (int)g[a].size()); mx = max(mx, (int)g[b].size()); } if(mx <= 2){ puts("1"); } else { for(int i = 1; i <= n; ++ i){ if(g[i].size() != 2){ for(int j : g[i]){ dfs(j, i, i); } } } int ans = 0; for(int i = 1; i <= n; ++ i){ int nw = -1; for(int j : h[i]){ if(h[j].size() == 1){ ++ nw; } } ans += max(0, nw); } printf("%d\n", ans); } }
5. AGC028D - Chords
首先断环为链,统计每个连通块出现次数,那么一对
定义
显然有当
最后总方案数是
点击查看代码
//AT_agc028_d #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 610; const ll P = 1e9 + 7; ll f[N][N], g[N], ans; int n, k, to[N], c[N][N], ok[N][N]; void solve(){ scanf("%d%d", &n, &k); for(int i = 1; i <= k; ++ i){ int a, b; scanf("%d%d", &a, &b); to[a] = b; to[b] = a; } for(int i = 1; i <= n+n; ++ i){ int mn = n+n+1, mx = 0, cnt = 0; for(int j = i; j <= n+n; ++ j){ if(to[j]){ mn = min(mn, to[j]); mx = max(mx, to[j]); ++ cnt; } c[i][j] = j - i + 1 - cnt; if(i <= mn && mx <= j){ ok[i][j] = 1; } } } g[0] = 1; for(int i = 2; i <= n+n; ++ i){ g[i] = g[i-2] * (i-1) % P; } for(int len = 2; len <= n+n; len += 2){ for(int i = 1; i + len - 1 <= n+n; ++ i){ int j = i + len - 1; if(!ok[i][j]){ continue; } f[i][j] = g[c[i][j]]; for(int k = i+1; k < j; k += 2){ if(!ok[i][k]){ continue; } f[i][j] -= f[i][k] * g[c[k+1][j]] % P; f[i][j] = (f[i][j] + P) % P; } ans += f[i][j] * g[c[1][i-1]+c[j+1][n+n]] % P; ans %= P; } } printf("%lld\n", ans); }
6. kupc2013 - タイル置き
如何在患有不会计数症的情况下通过 AT_kupc2013_j タイル置き
设
观察到
观察到
观察到
于是猜测
但是观察到
所以当
为多项式时,首先预处理出
然后就做完了。
点击查看代码
//AT_kupc2013_j #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll P = 1e9 + 7; const ll i2 = 500000004; const ll i3 = 333333336; const ll i4 = 250000002; const ll i5 = 400000003; const ll i6 = 166666668; const ll i8 = 125000001; const ll i10 = i2 * i5 % P; const ll i12 = 83333334; const ll i15 = i3 * i5 % P; const ll i24 = 41666667; const ll i120 = i10 * i12 % P; ll h, w; int n; namespace solve1{ ll f[13][1<<10][13]; ll main(){ memset(f, 0, sizeof(f)); f[0][0][0] = 1; for(int i = 1; i <= w; ++ i){ for(int pr = 0; pr <= n; ++ pr){ for(int j = 0; j < (1 << h); ++ j){ int fj = (1<<h)-1-j; for(int k = fj; ; k = (k-1) & fj){ int fk = j|k; fk = (1<<h)-1-fk; for(int l = fk; ; l = (l-1) & fk){ if((fk | (l<<1) | l) == fk && (l&(l<<1)) == 0){ int nw = __builtin_popcount(k|l); // printf("%d %d %d %d %d\n", (j|k|l|(l<<1))-(j^k^l^(l<<1)), j, k, l, l<<1); if(pr+nw <= n) f[i][k][pr+nw] = (f[i][k][pr+nw] + f[i-1][j][pr]) % P; }if(!l) break;} if(!k) break;} } } } // printf("%lld,", f[w][0][n]); return f[w][0][n]; } } namespace solve2{ struct mat{ ll a[256][256]; } a; int ys[32][8], cnt; mat mul(mat &x, mat &y){ mat z; for(int i = 0; i < cnt; ++ i) for(int j = 0; j < cnt; ++ j) z.a[i][j] = 0; for(int k = 0; k < cnt; ++ k){ for(int i = 0; i < cnt; ++ i){ for(int j = 0; j < cnt; ++ j){ z.a[i][j] = (z.a[i][j] + x.a[i][k] * y.a[k][j]) % P; } } } return z; } mat qp(mat x, int v){ mat z = x; -- v; while(v){ if(v & 1) z = mul(z, x); x = mul(x, x); v >>= 1; } return z; } int main(){ cnt = 0; for(int i = 0; i < (1 << h); ++ i){ for(int j = 0; j <= n; ++ j){ ys[i][j] = cnt ++; } } for(int pr = 0; pr <= n; ++ pr){ for(int j = 0; j < (1 << h); ++ j){ int fj = (1<<h)-1-j; for(int k = fj; ; k = (k-1) & fj){ int fk = j|k; fk = (1<<h)-1-fk; for(int l = fk; ; l = (l-1) & fk){ if((fk | (l<<1) | l) == fk && (l&(l<<1)) == 0){ int nw = __builtin_popcount(k|l); // printf("%d %d %d %d %d\n", (j|k|l|(l<<1))-(j^k^l^(l<<1)), j, k, l, l<<1); if(pr+nw <= n) ++ a.a[ys[j][pr]][ys[k][pr+nw]]; }if(!l) break;} if(!k) break;} } } a = qp(a, w); printf("%lld\n", a.a[ys[0][0]][ys[0][n]]); return 0; } } namespace solven3{ int main(){ ll x = h - 2, y = w - 2; ll f3 = (4*i3%P*y%P*y%P*y%P + 6 *y%P*y%P + 9 *y%P + 9*i2%P ) % P; ll f2 = (6 *y%P*y%P*y%P + 12 *y%P*y%P + 3*i2%P *y%P - 9*i2%P + P ) % P; ll f1 = (9 *y%P*y%P*y%P + 3*i2%P*y%P*y%P + 17*i3%P*y%P + 4 ) % P; ll f0 = (9*i2%P*y%P*y%P*y%P - 9*i2%P*y%P*y%P + 4 *y%P - 4 + P + P) % P; ll ans = f3 * x % P * x % P * x % P; ans += f2 * x % P * x % P; ans += f1 * x % P; ans += f0; printf("%lld\n", ans % P); return 0; } } namespace solven4{ int main(){ ll x = h - 3, y = w - 3; ll f4 = (2*i3%P *y%P*y%P*y%P*y%P + 20*i3%P *y%P*y%P*y%P + 25 *y%P*y%P + 125*i3%P *y%P + 625*i24%P ) % P; ll f3 = (20*i3%P *y%P*y%P*y%P*y%P + 52 *y%P*y%P*y%P + 146 *y%P*y%P + 515*i3%P *y%P + 275*i4%P ) % P; ll f2 = (25 *y%P*y%P*y%P*y%P + 146 *y%P*y%P*y%P + 3757*i12%P*y%P*y%P + 3677*i12%P*y%P + 2843*i24%P) % P; ll f1 = (125*i3%P *y%P*y%P*y%P*y%P + 515*i3%P*y%P*y%P*y%P + 3677*i12%P*y%P*y%P + 2933*i12%P*y%P + 275*i4%P ) % P; ll f0 = (625*i24%P*y%P*y%P*y%P*y%P + 275*i4%P*y%P*y%P*y%P + 2843*i24%P*y%P*y%P + 275*i4%P *y%P + 24 ) % P; ll ans = f4 * x % P * x % P * x % P * x % P; ans += f3 * x % P * x % P * x % P; ans += f2 * x % P * x % P; ans += f1 * x % P; ans += f0; printf("%lld\n", ans % P); return 0; } } namespace solven5{ int main(){ ll x = h - 4, y = w - 4; ll f5 = (4*i15%P *y%P*y%P*y%P*y%P*y%P + 14*i3%P *y%P*y%P*y%P*y%P + 98*i3%P *y%P*y%P*y%P + 343*i3%P *y%P*y%P + 2401*i12%P *y%P + 16807*i120%P) % P; ll f4 = (14*i3%P *y%P*y%P*y%P*y%P*y%P + 72 *y%P*y%P*y%P*y%P + 1321*i3%P *y%P*y%P*y%P + 3997*i3%P *y%P*y%P + 15925*i8%P *y%P + 14063*i12%P ) % P; ll f3 = (98*i3%P *y%P*y%P*y%P*y%P*y%P + 1321*i3%P *y%P*y%P*y%P*y%P + 4707*i2%P *y%P*y%P*y%P + 74977*i12%P *y%P*y%P + 99137*i12%P *y%P + 34895*i8%P ) % P; ll f2 = (343*i3%P *y%P*y%P*y%P*y%P*y%P + 3997*i3%P *y%P*y%P*y%P*y%P + 74977*i12%P*y%P*y%P*y%P + 44350*i3%P *y%P*y%P + 422351*i24%P*y%P + 100897*i12%P) % P; ll f1 = (2401*i12%P *y%P*y%P*y%P*y%P*y%P + 15925*i8%P *y%P*y%P*y%P*y%P + 99137*i12%P*y%P*y%P*y%P + 422351*i24%P*y%P*y%P + 190659*i10%P*y%P + 125521*i15%P) % P; ll f0 = (16807*i120%P*y%P*y%P*y%P*y%P*y%P + 14063*i12%P*y%P*y%P*y%P*y%P + 34895*i8%P *y%P*y%P*y%P + 100897*i12%P*y%P*y%P + 125521*i15%P*y%P + 3380 ) % P; ll ans = f5 * x % P * x % P * x % P * x % P * x % P; ans += f4 * x % P * x % P * x % P * x % P; ans += f3 * x % P * x % P * x % P; ans += f2 * x % P * x % P; ans += f1 * x % P; ans += f0; printf("%lld\n", ans % P); return 0; } } int main(){ freopen("domino.in", "r", stdin); freopen("domino.out", "w", stdout); scanf("%lld%lld%d", &h, &w, &n); if(h > w) swap(h, w); if(n == 1){ printf("%lld\n", (2*h*w-h-w)%P); } else if(n == 2){ ll x = (2*h*w-h-w) % P; ll y = (h*w-h-w+1) % P; ll ans = x*x%P-x-8*y%P; if(h > 1){ ans -= 2*w*(h-2)%P; } if(w > 1){ ans -= 2*h*(w-2)%P; } ans = ans * i2 % P; ans = (ans % P + P) % P; printf("%lld\n", ans); } else if(w <= 10){ printf("%lld\n", solve1::main()); } else if(h <= 5){ solve2::main(); } else if(n == 3){ solven3::main(); } else if(n == 4){ solven4::main(); } else if(n == 5){ solven5::main(); } return 0; }
7. NOI2020 - 命运
设
有
进行线段树合并,传参过程维护
点击查看代码
//P6773 #include <bits/stdc++.h> using namespace std; const int N = 5e5 + 10; typedef long long ll; const ll P = 998244353; int n, m; vector<int> g[N]; int dep[N], top[N]; void dfs(int x, int fa){ dep[x] = dep[fa] + 1; for(int i : g[x]){ if(i != fa){ dfs(i, x); } } } struct node{ ll sum, mul; int ls, rs; } t[N*40]; int cnt, rt[N]; void psd(int p){ if(t[p].ls){ t[t[p].ls].sum = (t[t[p].ls].sum * t[p].mul) % P; t[t[p].ls].mul = (t[t[p].ls].mul * t[p].mul) % P; } if(t[p].rs){ t[t[p].rs].sum = (t[t[p].rs].sum * t[p].mul) % P; t[t[p].rs].mul = (t[t[p].rs].mul * t[p].mul) % P; } t[p].mul = 1; } void add(int &p, int l, int r, int x, int v){ if(!p){ p = ++ cnt; t[p].mul = 1; } if(l == r){ t[p].sum = v; } else { int mid = l + r >> 1; if(x <= mid){ add(t[p].ls, l, mid, x, v); } else { add(t[p].rs, mid+1, r, x, v); } t[p].sum = (t[t[p].ls].sum + t[t[p].rs].sum) % P; } } ll qry(int p, int l, int r, int ql, int qr){ if(!p || qr < l || r < ql){ return 0; } else if(ql <= l && r <= qr){ return t[p].sum; } else { int mid = l + r >> 1; psd(p); return (qry(t[p].ls, l, mid, ql, qr) + qry(t[p].rs, mid+1, r, ql, qr)) % P; } } void mg(int &p, int q, int l, int r, ll &x, ll &y){ if(!p && !q){ return; } else if(!p){ x = (x + t[q].sum) % P; t[q].sum = (t[q].sum * y) % P; t[q].mul = (t[q].mul * y) % P; p += q; } else if(!q){ y = (y + t[p].sum) % P; t[p].sum = (t[p].sum * x) % P; t[p].mul = (t[p].mul * x) % P; } else if(l == r){ ll tp = t[p].sum, tq = t[q].sum; x = (x + tq) % P; t[p].sum = (t[p].sum * x + t[q].sum * y) % P; y = (y + tp) % P; } else { psd(p); psd(q); int mid = l + r >> 1; mg(t[p].ls, t[q].ls, l, mid, x, y); mg(t[p].rs, t[q].rs, mid+1, r, x, y); t[p].sum = (t[t[p].ls].sum + t[t[p].rs].sum) % P; } } void dfss(int x, int fa){ add(rt[x], 0, n, top[x], 1); for(int i : g[x]){ if(i != fa){ dfss(i, x); ll val = qry(rt[i], 0, n, 0, dep[x]), vv = 0; mg(rt[x], rt[i], 0, n, val, vv); } } } int main(){ scanf("%d", &n); for(int i = 1; i < n; ++ i){ int x, y; scanf("%d%d", &x, &y); g[x].push_back(y); g[y].push_back(x); } scanf("%d", &m); dfs(1, 0); for(int i = 1; i <= m; ++ i){ int x, y; scanf("%d%d", &x, &y); if(dep[x] < dep[y]){ swap(x, y); top[x] = max(top[x], dep[y]); } } dfss(1, 0); printf("%lld\n", qry(rt[1], 0, n, 0, 0)); return 0; }
8. JOI Open 2017 - 推土机
考虑答案斜率只有可能为给定点中两点连线斜率,以及这个斜率稍微偏一点点(如样例 3),所以预处理所有斜率并排序,维护一个数组表示以这个斜率从下往上扫到每一个点的顺序。那么答案就为这个数组每个时刻的最大子段和。
对于斜率需稍微偏一点点的情况,可以发现仍然对应的是数组中的某个子段;每个子段也都能找到一个给定斜率,所以做法是正确的。
对于每个点对,它们之间在数组中的先后关系一定至多两种且一定只会变化至多一次,故可以预处理出这一次变化时间,使用单点修改、查询最大子段和的线段树维护数组即可。
但是会有一个问题:若存在多点共线,不妨为
若以
经过打表得知可以使得排列 rev 的变化顺序数量为 A092238,但不是本题重点。
可以证明若原排列为
时间复杂度
点击查看代码
//P10630 #include <bits/stdc++.h> using namespace std; const int N = 2010; int n, x[N], y[N], w[N]; int cnt, pos[N]; map<int, int> mp; vector<pair<int, int> > cg[N*N]; int tmp[N], tmpp[N]; typedef long long ll; double p[N*N]; struct tree{ ll am, lm, rm, sum; } t[N*4]; void psu(tree &p, tree ls, tree rs){ p.sum = ls.sum + rs.sum; p.lm = max(ls.lm, ls.sum + rs.lm); p.rm = max(rs.rm, rs.sum + ls.rm); p.am = max(ls.am, rs.am); p.am = max(p.am, ls.rm + rs.lm); } void add(int p, int l, int r, int x, int v){ if(l == r){ tmp[x] = p; t[p].sum = v; t[p].am = t[p].lm = t[p].rm = max(0, v); } else { int mid = l + r >> 1; if(x <= mid){ add(p<<1, l, mid, x, v); } else { add(p<<1|1, mid+1, r, x, v); } psu(t[p], t[p<<1], t[p<<1|1]); } } ll ans = 0; struct node{ int x, y, w, id; } a[N]; int st = 0; bool cmp(node c, node d){ double cc = c.y - c.x * p[st]; double dd = d.y - d.x * p[st]; return fabs(cc-dd) <= 1e-8 ? c.x < d.x : cc<dd; } bool cmq(node c, node d, int op){ double cc = c.y - c.x * p[op]; double dd = d.y - d.x * p[op]; return fabs(cc-dd) <= 1e-8 ? c.x < d.x : cc<dd; } int main(){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%d%d%d", &x[i], &y[i], &w[i]); a[i] = { x[i], y[i], w[i], i }; } for(int i = 1; i <= n; ++ i){ for(int j = i + 1; j <= n; ++ j){ if(x[i] != x[j]){ double sl = (y[j] - y[i]) * 1.0 / (x[j] - x[i]); p[++cnt] = sl; } } } sort(p + 1, p + cnt + 1); p[0] = p[1] - 1; int m = unique(p, p + cnt + 1) - p; sort(a + 1, a + n + 1, cmp); for(int i = 1; i <= n; ++ i){ pos[a[i].id] = i; } for(int i = 1; i <= n; ++ i){ for(int j = i + 1; j <= n; ++ j){ if(a[i].x != a[j].x){ double sl = (a[j].y - a[i].y) * 1.0 / (a[j].x - a[i].x); int ps = lower_bound(p, p + m + 1, sl) - p; cg[ps].push_back(make_pair(a[i].id, a[j].id)); } } } for(int i = 1; i <= n; ++ i){ add(1, 1, n, pos[i], w[i]); } for(int i = 0; i <= m; ++ i){ ans = max(ans, t[1].am); for(auto j : cg[i]){ int x = j.first, y = j.second; add(1, 1, n, pos[x], w[y]); add(1, 1, n, pos[y], w[x]); swap(pos[x], pos[y]); } } ans = max(ans, t[1].am); printf("%lld\n", ans); }
9. NOI Online #2 提高组 - 游戏
设
考虑 dp 求出
点击查看代码
//P6478 #include <bits/stdc++.h> using namespace std; const int N = 5010; typedef long long ll; const ll P = 998244353; int n; vector<int> g[N]; int sc[N][2], sz[N]; char s[N]; ll f[N][N], tmp[N]; ll C[N][N]; ll fac[N]; void dfs(int x, int fa){ sc[x][0] += s[x] == '0'; sc[x][1] += s[x] == '1'; f[x][0] = 1; sz[x] = 1; for(int i : g[x]){ if(i != fa){ dfs(i, x); for(int j = (sz[x] - 1) / 2; j >= 0; -- j){ for(int k = sz[i] / 2; k >= 0; -- k){ tmp[j+k] = (tmp[j+k] + f[x][j] * f[i][k]) % P; } } for(int j = (sz[x] - 1) / 2 + sz[i] / 2; j >= 0; -- j){ f[x][j] = tmp[j]; tmp[j] = 0; } sz[x] += sz[i]; sc[x][0] += sc[i][0]; sc[x][1] += sc[i][1]; } } for(int i = sz[x] / 2; i > 0; -- i){ f[x][i] = (f[x][i] + f[x][i-1] * (sc[x]['1'-s[x]] - i + 1)) % P; } } int main(){ scanf("%d", &n); scanf("%s", s+1); for(int i = 1; i < n; ++ i){ int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs(1, 0); C[0][0] = fac[0] = 1; for(int i = 1; i <= n; ++ i){ C[i][0] = 1; fac[i] = fac[i-1] * i % P; for(int j = 1; j <= i; ++ j){ C[i][j] = (C[i-1][j-1] + C[i-1][j]) % P; } } for(int i = 0; i <= n/2; ++ i){ ll ans = 0; for(int j = i; j <= n/2; ++ j){ ll val = C[j][i] * f[1][j] % P * fac[n/2-j] % P; if((j-i) & 1){ val = val * (P-1) % P; } ans = (ans + val) % P; } printf("%lld\n", ans); } return 0; }
10. NEERC2016 - Mole Tunnels
显然有费用流模型,考虑模拟费用流。
维护
点击查看代码
//P6122 #include <bits/stdc++.h> using namespace std; const int N = 2e5 + 10; int n, m, c[N], p; int f[N], g[N]; int eg[N]; #define vdn(i) (eg[i]>0 ? -1 : 1) #define vup(i) (eg[i]<0 ? -1 : 1) void upd(int x){ f[x] = 0x3f3f3f3f; if(f[x<<1] + vdn(x<<1) < f[x]){ f[x] = f[x<<1] + vdn(x<<1); g[x] = g[x<<1]; } if(f[x<<1|1] + vdn(x<<1|1) < f[x]){ f[x] = f[x<<1|1] + vdn(x<<1|1); g[x] = g[x<<1|1]; } if(c[x] && f[x] > 0){ f[x] = 0; g[x] = x; } } signed main(){ memset(f, 0x3f, sizeof(f)); scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++ i){ scanf("%d", &c[i]); } for(int i = n; i >= 1; -- i){ upd(i); } long long la = 0; for(int i = 1; i <= m; ++ i){ scanf("%d", &p); int x = p, mn = 2e9, ps = 0, nw = 0; while(x){ if(f[x] + nw < mn){ mn = f[x] + nw; ps = x; } nw += vup(x); x >>= 1; } la += mn; printf("%lld ", la); x = p; while(x != ps){ ++ eg[x]; upd(x); x >>= 1; } x = g[ps]; -- c[x]; while(x != ps){ -- eg[x]; upd(x); x >>= 1; } while(ps){ upd(ps); ps >>= 1; } } return 0; }
11. AHOI2022 - 排列
连边
设共有
考虑将相同的
答案转化为:
需要支持
点击查看代码
//P8338 #include <bits/stdc++.h> using namespace std; const int N = 5e5 + 10; int T, n, m, per[N], col[N], a[N]; typedef long long ll; const ll P = 1e9 + 7; ll ans; int b[N], c[N], er[N]; pair<int, int> pr[N][3]; vector<int> cg[N]; void upd(pair<int, int> p, int i){ if(p >= pr[i][0]){ pr[i][2] = pr[i][1]; pr[i][1] = pr[i][0]; pr[i][0] = p; } else if(p >= pr[i][1]){ pr[i][2] = pr[i][1]; pr[i][1] = p; } else { pr[i][2] = max(pr[i][2], p); } } ll qp(ll x, ll y){ ll ans = 1; while(y){ if(y & 1){ ans = ans * x % P; } x = x * x % P; y >>= 1; } return ans; } void dvv(ll &val, int tim, int vl){ ll q = qp(vl, P-2); while(tim--){ val = val * q % P; } } int main(){ scanf("%d", &T); while(T--){ scanf("%d", &m); for(int i = 1; i <= m; ++ i){ scanf("%d", &per[i]); } for(int i = 1; i <= m; ++ i){ if(!col[i]){ col[i] = ++ n; ++ a[n]; int st = per[i]; while(st != i){ col[st] = n; st = per[st]; ++ a[n]; } b[n] = a[n]; } } sort(a + 1, a + n + 1); sort(b + 1, b + n + 1); int tot = unique(b + 1, b + n + 1) - b - 1; for(int i = 1, j = 1; i <= tot; ++ i){ while(j <= n && a[j] == b[i]){ ++ j; ++ c[i]; } int x = b[i]; for(int y = 2; y * y <= x; ++ y){ if(x % y == 0){ int cy = 0; while(x % y == 0){ x /= y; ++ cy; } pair<int, int> p = make_pair(cy, i); upd(p, y); if(y == 2){ er[i] = cy; } } } if(x){ upd(make_pair(1, i), x); } } ll ml = 1; for(int i = 2; i <= m; ++ i){ cg[pr[i][0].second].push_back(i); for(int j = 1; j <= pr[i][0].first; ++ j){ ml = ml * i % P; } } n = tot; for(int i = 1; i <= n; ++ i){ for(int j = i + 1; j <= n; ++ j){ ll tmp = 2 * c[i] * c[j] % P * b[i] % P * b[j] % P; ll mll = ml; if(c[i] == 1){ for(int p : cg[i]){ if(pr[p][1].second == j && c[j] == 1){ dvv(mll, pr[p][0].first - pr[p][2].first, p); } else { dvv(mll, pr[p][0].first - pr[p][1].first, p); } } } if(c[j] == 1){ for(int p : cg[j]){ if(pr[p][1].second == i && c[i] == 1){ dvv(mll, pr[p][0].first - pr[p][2].first, p); } else { dvv(mll, pr[p][0].first - pr[p][1].first, p); } } } int vv = b[i] + b[j]; for(int x = 2; x * x <= vv; ++ x){ if(vv % x == 0){ int y = 0; while(vv % x == 0){ vv /= x; ++ y; } int st; if((pr[x][0].second!=i||c[i]-1) && (pr[x][0].second!=j||c[j]-1)){ st = pr[x][0].first; } else if((pr[x][1].second!=i||c[i]-1) && (pr[x][1].second!=j||c[j]-1)){ st = pr[x][1].first; } else { st = pr[x][2].first; } for(int z = st; z < y; ++ z){ mll = mll * x % P; } } } if(vv != 1){ int x = vv; int st; if((pr[x][0].second!=i||c[i]-1) && (pr[x][0].second!=j||c[j]-1)){ st = pr[x][0].first; } else if((pr[x][1].second!=i||c[i]-1) && (pr[x][1].second!=j||c[j]-1)){ st = pr[x][1].first; } else { st = pr[x][2].first; } for(int z = st; z < 1; ++ z){ mll = mll * x % P; } } ans = (ans + tmp * mll) % P; } } for(int i = 1; i <= n; ++ i){ ll tmp = c[i] * (c[i]-1) % P * b[i] % P * b[i] % P; ll mll = ml; if(er[i] == pr[2][0].first){ mll = mll * 2 % P; } ans = (ans + tmp * mll % P) % P; } printf("%lld\n", ans); for(int i = 0; i <= m; ++ i){ per[i] = col[i] = a[i] = b[i] = c[i] = er[i] = 0; memset(pr[i], 0, sizeof(pr[i])); vector<int> ().swap(cg[i]); } n = m = ans = 0; } return 0; }
12. bzoj - 最假女选手
seg beats 区间最值。套路是维护区间最值、区间次最值、区间最值数量。可以证明复杂度为
本题线段树每一个节点维护:最大值、次大值、最大值数量、最小值、次小值、最小值数量、区间 max tag、区间 min tag、区间 add tag、区间和。
pushdown 时先 add 再 max/min;pushup add 时更新 max/min tag。
注意 pushup max/min 的时候,对于区间内只有
点击查看代码
//P10639 #include <bits/stdc++.h> using namespace std; const int N = 5e5 + 10, inf = 2e9; typedef long long ll; int n, m, a[N]; namespace FastIO { #if __cplusplus > 201700 #define INLINE_V inline #else #define INLINE_V #endif #if (defined(LOCAL) || defined(_WIN32)) && !defined(DISABLE_MMAP) #define DISABLE_MMAP #endif #ifndef DISABLE_MMAP #include <sys/mman.h> #endif INLINE_V constexpr int _READ_SIZE = 1 << 18; INLINE_V static char _read_buffer[_READ_SIZE], *_read_ptr = nullptr, *_read_ptr_end = nullptr; inline char gc() { if (__builtin_expect(_read_ptr == _read_ptr_end, false)) { _read_ptr = _read_buffer; _read_ptr_end = _read_buffer + fread(_read_buffer, 1, _READ_SIZE, stdin); if (__builtin_expect(_read_ptr == _read_ptr_end, false)) return EOF;} return *_read_ptr++; } INLINE_V constexpr int _WRITE_SIZE = 1 << 18; INLINE_V static char _write_buffer[_WRITE_SIZE], *_write_ptr = _write_buffer; inline void pc(char c) { *_write_ptr++ = c; if (__builtin_expect(_write_buffer + _WRITE_SIZE == _write_ptr, false)) { fwrite(_write_buffer, 1, _write_ptr - _write_buffer, stdout); _write_ptr = _write_buffer; } } INLINE_V struct _auto_flush { ~_auto_flush() { fwrite(_write_buffer, 1, _write_ptr - _write_buffer, stdout); } } _auto_flush; inline bool _isdigit(char c) { return (c & 16) && c != EOF; } inline bool _isgraph(char c) { return c > 32 && c != EOF; } template <class T> INLINE_V constexpr bool _is_integer = numeric_limits<T>::is_integer; template <class T> INLINE_V constexpr bool _is_signed = numeric_limits<T>::is_signed; template <class T> INLINE_V constexpr bool _is_unsigned = _is_integer<T> && !_is_signed<T>; template <> INLINE_V constexpr bool _is_integer<__int128> = true; template <> INLINE_V constexpr bool _is_integer<__uint128_t> = true; template <> INLINE_V constexpr bool _is_signed<__int128> = true; template <> INLINE_V constexpr bool _is_unsigned<__uint128_t> = true; inline void read(char &c) { do c = gc(); while (!_isgraph(c)); } inline void read_cstr(char *s) { char c = gc(); while (!_isgraph(c)) c = gc(); while (_isgraph(c)) *s++ = c, c = gc(); *s = 0; } inline void read(string &s) { char c = gc(); s.clear(); while (!_isgraph(c)) c = gc(); while (_isgraph(c)) s.push_back(c), c = gc(); } template <class T, enable_if_t<_is_signed<T>, int> = 0> inline void read(T &x) { char c = gc(); bool f = true; x = 0; while (!_isdigit(c)) { if (c == 45) f = false; c = gc(); } if (f) while (_isdigit(c)) x = x * 10 + (c & 15), c = gc(); else while (_isdigit(c)) x = x * 10 - (c & 15), c = gc(); } template <class T, enable_if_t<_is_unsigned<T>, int> = 0> inline void read(T &x) { char c = gc(); while (!_isdigit(c)) c = gc(); x = 0; while (_isdigit(c)) x = x * 10 + (c & 15), c = gc(); } inline void write(char c) { pc(c); } inline void write_cstr(const char *s) { while (*s) pc(*s++); } inline void write(const string &s) { for (char c : s) pc(c); } template <class T, enable_if_t<_is_signed<T>, int> = 0> inline void write(T x) { char buffer[numeric_limits<T>::digits10 + 1]; int digits = 0; if (x >= 0) do buffer[digits++] = (x % 10) | 48, x /= 10; while (x); else { pc(45); do buffer[digits++] = -(x % 10) | 48, x /= 10; while (x); } while (digits) pc(buffer[--digits]); } template <class T, enable_if_t<_is_unsigned<T>, int> = 0> inline void write(T x) { char buffer[numeric_limits<T>::digits10 + 1]; int digits = 0; do buffer[digits++] = (x % 10) | 48, x /= 10; while (x); while (digits) pc(buffer[--digits]); } template <int N> struct _tuple_io_helper { template <class... T> static inline void _read(tuple<T...> &x) { _tuple_io_helper<N - 1>::_read(x), read(get<N - 1>(x)); } template <class... T> static inline void _write(const tuple<T...> &x) { _tuple_io_helper<N - 1>::_write(x), pc(32), write(get<N - 1>(x)); } }; template <> struct _tuple_io_helper<1> { template <class... T> static inline void _read(tuple<T...> &x) { read(get<0>(x)); } template <class... T> static inline void _write(const tuple<T...> &x) { write(get<0>(x)); } }; template <class... T> inline void read(tuple<T...> &x) { _tuple_io_helper<sizeof...(T)>::_read(x); } template <class... T> inline void write(const tuple<T...> &x) { _tuple_io_helper<sizeof...(T)>::_write(x); } template <class T1, class T2> inline void read(pair<T1, T2> &x) { read(x.first), read(x.second); } template <class T1, class T2> inline void write(const pair<T1, T2> &x) { write(x.first), pc(32), write(x.second); } template <class T1, class... T2> inline void read(T1 &x, T2 &...y) { read(x), read(y...); } template <class... T> inline void read_cstr(char *x, T *...y) { read_cstr(x), read_cstr(y...); } template <class T1, class... T2> inline void write(const T1 &x, const T2 &...y) { write(x), write(y...); } template <class... T> inline void write_cstr(const char *x, const T *...y) { write_cstr(x), write_cstr(y...); } template <class T> inline void print(const T &x) { write(x); } inline void print_cstr(const char *x) { write_cstr(x); } template <class T1, class... T2> inline void print(const T1 &x, const T2 &...y) { print(x), pc(32), print(y...); } template <class... T> inline void print_cstr(const char *x, const T *...y) { print_cstr(x), pc(32), print_cstr(y...); } inline void println() { pc(10); } inline void println_cstr() { pc(10); } template <class... T> inline void println(const T &...x) { print(x...), pc(10); } template <class... T> inline void printk(const T &...x) { print(x...), pc(32); } template <class... T> inline void println_cstr(const T *...x) { print_cstr(x...), pc(10); } } using namespace FastIO; struct node{ ll sum; //区间和 int mx; //最大值 int smx; //次大值 int mxc; //最大值数量 int mn; //最小值 int smn; //次小值 int mnc; //最小值数量 int tmx; //取max tag int tmn; //取min tag int tpl; //加法 tag } t[N*4]; inline void psu(int p){ #define ls p<<1 #define rs p<<1|1 t[p].sum = t[ls].sum + t[rs].sum; if(t[ls].mx == t[rs].mx){ t[p].mx = t[ls].mx; t[p].smx = max(t[ls].smx, t[rs].smx); t[p].mxc = t[ls].mxc + t[rs].mxc; } else if(t[ls].mx > t[rs].mx){ t[p].mx = t[ls].mx; t[p].smx = max(t[ls].smx, t[rs].mx); t[p].mxc = t[ls].mxc; } else { t[p].mx = t[rs].mx; t[p].smx = max(t[rs].smx, t[ls].mx); t[p].mxc = t[rs].mxc; } if(t[ls].mn == t[rs].mn){ t[p].mn = t[ls].mn; t[p].smn = min(t[ls].smn, t[rs].smn); t[p].mnc = t[ls].mnc + t[rs].mnc; } else if(t[ls].mn < t[rs].mn){ t[p].mn = t[ls].mn; t[p].smn = min(t[ls].smn, t[rs].mn); t[p].mnc = t[ls].mnc; } else { t[p].mn = t[rs].mn; t[p].smn = min(t[rs].smn, t[ls].mn); t[p].mnc = t[rs].mnc; } } inline void spp(int p, int l, int r, int v){ if(v){ t[p].sum += (ll)v * (r - l + 1); t[p].mx += v; if(t[p].smx != -inf){ t[p].smx += v; } t[p].mn += v; if(t[p].smn != inf){ t[p].smn += v; } if(t[p].tmx != -inf){ t[p].tmx += v; } if(t[p].tmn != inf){ t[p].tmn += v; } t[p].tpl += v; } } inline void spx(int p, int v){ if(t[p].mn < v){ t[p].sum += (ll)(v - t[p].mn) * t[p].mnc; if(t[p].smx == t[p].mn){ t[p].smx = v; } if(t[p].mx == t[p].mn){ t[p].mx = v; } t[p].tmn = max(t[p].tmn, v); t[p].mn = v; t[p].tmx = v; } } inline void spn(int p, int v){ if(t[p].mx > v){ t[p].sum -= (ll)(t[p].mx - v) * t[p].mxc; if(t[p].smn == t[p].mx){ t[p].smn = v; } if(t[p].mn == t[p].mx){ t[p].mn = v; } t[p].tmx = min(t[p].tmx, v); t[p].mx = v; t[p].tmn = v; } } inline void psd(int p, int l, int r){ #define ls p<<1 #define rs p<<1|1 int mid = l + r >> 1; spp(ls, l, mid, t[p].tpl); spp(rs, mid+1, r, t[p].tpl); if(t[p].tmx != -inf){ spx(ls, t[p].tmx); spx(rs, t[p].tmx); } if(t[p].tmn != inf){ spn(ls, t[p].tmn); spn(rs, t[p].tmn); } t[p].tpl = 0; t[p].tmx = -inf; t[p].tmn = inf; } inline void build(int p, int l, int r){ t[p].tmx = -inf; t[p].tmn = inf; if(l == r){ t[p].mx = t[p].mn = t[p].sum = a[l]; t[p].smx = -inf; t[p].smn = inf; t[p].mxc = t[p].mnc = 1; } else { int mid = l + r >> 1; build(p<<1, l, mid); build(p<<1|1, mid+1, r); psu(p); } } inline void add(int p, int l, int r, int ql, int qr, int v){ if(qr < l || r < ql){ return; } else if(ql <= l && r <= qr){ spp(p, l, r, v); } else { int mid = l + r >> 1; psd(p, l, r); add(p<<1, l, mid, ql, qr, v); add(p<<1|1, mid+1, r, ql, qr, v); psu(p); } } inline void cmx(int p, int l, int r, int ql, int qr, int v){ if(qr < l || r < ql || t[p].mn >= v){ return; } else if(ql <= l && r <= qr && t[p].smn > v){ spx(p, v); } else { int mid = l + r >> 1; psd(p, l, r); cmx(p<<1, l, mid, ql, qr, v); cmx(p<<1|1, mid+1, r, ql, qr, v); psu(p); } } inline void cmn(int p, int l, int r, int ql, int qr, int v){ if(qr < l || r < ql || t[p].mx <= v){ return; } else if(ql <= l && r <= qr && t[p].smx < v){ spn(p, v); } else { int mid = l + r >> 1; psd(p, l, r); cmn(p<<1, l, mid, ql, qr, v); cmn(p<<1|1, mid+1, r, ql, qr, v); psu(p); } } inline ll qsm(int p, int l, int r, int ql, int qr){ if(qr < l || r < ql){ return 0; } else if(ql <= l && r <= qr){ return t[p].sum; } else { int mid = l + r >> 1; psd(p, l, r); return qsm(p<<1, l, mid, ql, qr) + qsm(p<<1|1, mid+1, r, ql, qr); } } inline int qmx(int p, int l, int r, int ql, int qr){ if(qr < l || r < ql){ return -inf; } else if(ql <= l && r <= qr){ return t[p].mx; } else { int mid = l + r >> 1; psd(p, l, r); return max(qmx(p<<1, l, mid, ql, qr), qmx(p<<1|1, mid+1, r, ql, qr)); } } inline int qmn(int p, int l, int r, int ql, int qr){ if(qr < l || r < ql){ return inf; } else if(ql <= l && r <= qr){ return t[p].mn; } else { int mid = l + r >> 1; psd(p, l, r); return min(qmn(p<<1, l, mid, ql, qr), qmn(p<<1|1, mid+1, r, ql, qr)); } } int main(){ read(n); for(int i = 1; i <= n; ++ i){ read(a[i]); } build(1, 1, n); read(m); while(m--){ int op, l, r, x; read(op, l, r); if(op == 1){ read(x); add(1, 1, n, l, r, x); } else if(op == 2){ read(x); cmx(1, 1, n, l, r, x); } else if(op == 3){ read(x); cmn(1, 1, n, l, r, x); } else if(op == 4){ println(qsm(1, 1, n, l, r)); } else if(op == 5){ println(qmx(1, 1, n, l, r)); } else if(op == 6){ println(qmn(1, 1, n, l, r)); } } return 0; }
13. luogu - Scarlet loves WenHuaKe
发现可行方案一定每行
不放满不好处理,考虑转化模型,设
类别不同/无序集合不好处理,转化,设
枚举
上下界取严格一点得
发现可以用
变形得
设
则有
二项式定理得
点击查看代码
//P4831 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 2e5 + 10; const ll P = 998244353; const ll i2 = (P+1) / 2; int n, m; ll fac[N], inv[N], ip2[N]; ll qp(ll x, ll y){ ll ans = 1; while(y){ if(y & 1){ ans = ans * x % P; } x = x * x % P; y >>= 1; } return ans; } ll C(int x, int y){ return fac[x] * inv[y] % P * inv[x-y] % P; } void solve(){ scanf("%d%d", &n, &m); fac[0] = ip2[0] = 1; for(int i = 1; i <= m+m; ++ i){ ip2[i] = ip2[i-1] * i2 % P; fac[i] = fac[i-1] * i % P; } inv[m+m] = qp(fac[m+m], P-2); for(int i = m+m-1; i >= 0; -- i){ inv[i] = inv[i+1] * (i+1) % P; } ll f = 0; for(int i = 0; i <= n; ++ i){ ll g = 0; for(int j = max(n+n-m-i, 0); j <= n-i; ++ j){ g = (g + C(m-i, j) * C(m-i-j, 2*(n-i-j)) % P * ip2[j]) % P; } g = g * fac[2*(n-i)] % P; f = (f + ((i&1) ? P-1 : 1) * C(n, i) % P * g % P * inv[m-i]) % P; } f = f * fac[m] % P * ip2[n] % P; printf("%lld\n", f); }
14. CF1970A2/3 - Balanced Unshuffle
考虑求出排序后三元组的
设排序后
中左括号数量 中右括号数量; 时有 为右括号。
结论 1 成立,因为对于原串中一对匹配的括号
于是从左往右扫一遍
点击查看代码
//CF1970A3 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 5e5 + 10; int n, dep[N], st[N]; char c[N], s[N]; void solve(){ scanf("%s", c+1); n = strlen(c+1); for(int i = 1, nw = 0, lc = 0, nc = 0; i <= n; ++ i){ if(c[i] == '\('){ ++ nc; } else if(lc == 0){ ++ nw; lc = nc - 1; nc = 0; } else { -- lc; } dep[i] = nw; st[nw] = i; } for(int i = 1, nw = 0; i <= n; ++ i){ s[i] = c[st[nw]]; if(c[st[nw]] == '\('){ -- st[nw]; ++ nw; } else { -- st[nw]; -- nw; } } printf("%s", s+1); }
15. 十二省联考2019 - 春节十二响
考虑维护集合
点击查看代码
//P5290 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 2e5 + 10; int n, a[N], id[N], tot; vector<int> g[N]; priority_queue<int> st[N]; void dfs(int x){ for(int i : g[x]){ dfs(i); if(id[x] == 0){ id[x] = id[i]; continue; } int p = id[x], q = id[i]; if(st[p].size() < st[q].size()){ swap(p, q); } id[x] = p; vector<int> v; while(!st[q].empty()){ int ip = st[p].top(), iq = st[q].top(); st[p].pop(); st[q].pop(); v.push_back(max(ip, iq)); } for(int i : v){ st[p].push(i); } } if(!id[x]){ id[x] = ++ tot; } st[id[x]].push(a[x]); } void solve(){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); } for(int i = 2; i <= n; ++ i){ int f; scanf("%d", &f); g[f].push_back(i); } dfs(1); ll ans = 0; while(!st[id[1]].empty()){ ans += st[id[1]].top(); st[id[1]].pop(); } printf("%lld\n", ans); }
16. UNR #7 - 火星式选拔
考虑
考虑
所以我们的数据结构要支持查询
还有一个问题:查询
点击查看代码
//uoj812 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } namespace FastIO { #if __cplusplus > 201700 #define INLINE_V inline #else #define INLINE_V #endif #if (defined(LOCAL) || defined(_WIN32)) && !defined(DISABLE_MMAP) #define DISABLE_MMAP #endif #ifndef DISABLE_MMAP #include <sys/mman.h> #endif INLINE_V constexpr int _READ_SIZE = 1 << 18; INLINE_V static char _read_buffer[_READ_SIZE], *_read_ptr = nullptr, *_read_ptr_end = nullptr; inline char gc() { if (__builtin_expect(_read_ptr == _read_ptr_end, false)) { _read_ptr = _read_buffer; _read_ptr_end = _read_buffer + fread(_read_buffer, 1, _READ_SIZE, stdin); if (__builtin_expect(_read_ptr == _read_ptr_end, false)) return EOF;} return *_read_ptr++; } INLINE_V constexpr int _WRITE_SIZE = 1 << 18; INLINE_V static char _write_buffer[_WRITE_SIZE], *_write_ptr = _write_buffer; inline void pc(char c) { *_write_ptr++ = c; if (__builtin_expect(_write_buffer + _WRITE_SIZE == _write_ptr, false)) { fwrite(_write_buffer, 1, _write_ptr - _write_buffer, stdout); _write_ptr = _write_buffer; } } INLINE_V struct _auto_flush { ~_auto_flush() { fwrite(_write_buffer, 1, _write_ptr - _write_buffer, stdout); } } _auto_flush; inline bool _isdigit(char c) { return (c & 16) && c != EOF; } inline bool _isgraph(char c) { return c > 32 && c != EOF; } template <class T> INLINE_V constexpr bool _is_integer = numeric_limits<T>::is_integer; template <class T> INLINE_V constexpr bool _is_signed = numeric_limits<T>::is_signed; template <class T> INLINE_V constexpr bool _is_unsigned = _is_integer<T> && !_is_signed<T>; template <> INLINE_V constexpr bool _is_integer<__int128> = true; template <> INLINE_V constexpr bool _is_integer<__uint128_t> = true; template <> INLINE_V constexpr bool _is_signed<__int128> = true; template <> INLINE_V constexpr bool _is_unsigned<__uint128_t> = true; inline void read(char &c) { do c = gc(); while (!_isgraph(c)); } inline void read_cstr(char *s) { char c = gc(); while (!_isgraph(c)) c = gc(); while (_isgraph(c)) *s++ = c, c = gc(); *s = 0; } inline void read(string &s) { char c = gc(); s.clear(); while (!_isgraph(c)) c = gc(); while (_isgraph(c)) s.push_back(c), c = gc(); } template <class T, enable_if_t<_is_signed<T>, int> = 0> inline void read(T &x) { char c = gc(); bool f = true; x = 0; while (!_isdigit(c)) { if (c == 45) f = false; c = gc(); } if (f) while (_isdigit(c)) x = x * 10 + (c & 15), c = gc(); else while (_isdigit(c)) x = x * 10 - (c & 15), c = gc(); } template <class T, enable_if_t<_is_unsigned<T>, int> = 0> inline void read(T &x) { char c = gc(); while (!_isdigit(c)) c = gc(); x = 0; while (_isdigit(c)) x = x * 10 + (c & 15), c = gc(); } inline void write(char c) { pc(c); } inline void write_cstr(const char *s) { while (*s) pc(*s++); } inline void write(const string &s) { for (char c : s) pc(c); } template <class T, enable_if_t<_is_signed<T>, int> = 0> inline void write(T x) { char buffer[numeric_limits<T>::digits10 + 1]; int digits = 0; if (x >= 0) do buffer[digits++] = (x % 10) | 48, x /= 10; while (x); else { pc(45); do buffer[digits++] = -(x % 10) | 48, x /= 10; while (x); } while (digits) pc(buffer[--digits]); } template <class T, enable_if_t<_is_unsigned<T>, int> = 0> inline void write(T x) { char buffer[numeric_limits<T>::digits10 + 1]; int digits = 0; do buffer[digits++] = (x % 10) | 48, x /= 10; while (x); while (digits) pc(buffer[--digits]); } template <int N> struct _tuple_io_helper { template <class... T> static inline void _read(tuple<T...> &x) { _tuple_io_helper<N - 1>::_read(x), read(get<N - 1>(x)); } template <class... T> static inline void _write(const tuple<T...> &x) { _tuple_io_helper<N - 1>::_write(x), pc(32), write(get<N - 1>(x)); } }; template <> struct _tuple_io_helper<1> { template <class... T> static inline void _read(tuple<T...> &x) { read(get<0>(x)); } template <class... T> static inline void _write(const tuple<T...> &x) { write(get<0>(x)); } }; template <class... T> inline void read(tuple<T...> &x) { _tuple_io_helper<sizeof...(T)>::_read(x); } template <class... T> inline void write(const tuple<T...> &x) { _tuple_io_helper<sizeof...(T)>::_write(x); } template <class T1, class T2> inline void read(pair<T1, T2> &x) { read(x.first), read(x.second); } template <class T1, class T2> inline void write(const pair<T1, T2> &x) { write(x.first), pc(32), write(x.second); } template <class T1, class... T2> inline void read(T1 &x, T2 &...y) { read(x), read(y...); } template <class... T> inline void read_cstr(char *x, T *...y) { read_cstr(x), read_cstr(y...); } template <class T1, class... T2> inline void write(const T1 &x, const T2 &...y) { write(x), write(y...); } template <class... T> inline void write_cstr(const char *x, const T *...y) { write_cstr(x), write_cstr(y...); } template <class T> inline void print(const T &x) { write(x); } inline void print_cstr(const char *x) { write_cstr(x); } template <class T1, class... T2> inline void print(const T1 &x, const T2 &...y) { print(x), pc(32), print(y...); } template <class... T> inline void print_cstr(const char *x, const T *...y) { print_cstr(x), pc(32), print_cstr(y...); } inline void println() { pc(10); } inline void println_cstr() { pc(10); } template <class... T> inline void println(const T &...x) { print(x...), pc(10); } template <class... T> inline void printk(const T &...x) { print(x...), pc(32); } template <class... T> inline void println_cstr(const T *...x) { print_cstr(x...), pc(10); } } using namespace FastIO; const int N = 5e5 + 10; int n, q, a[N], b[N], c[N], fb[N]; int f[N][20], tq; ll ans[N]; struct qry{ int x, r, id; } qr[N], qrr[N]; vector<int> g[N]; struct seg1{ int t[N*4]; inline void build(int p, int l, int r){ t[p] = n+1; if(l != r){ int mid = l + r >> 1; build(p<<1, l, mid); build(p<<1|1, mid+1, r); } } inline int ask(int p, int l, int r, int ql, int qr){ if(qr < l || r < ql){ return n+1; } else if(ql <= l && r <= qr){ return t[p]; } else { int mid = l + r >> 1; return min(ask(p<<1, l, mid, ql, qr), ask(p<<1|1, mid+1, r, ql, qr)); } } inline void add(int p, int l, int r, int x, int v){ if(l == r){ t[p] = v; } else { int mid = l + r >> 1; if(x <= mid){ add(p<<1, l, mid, x, v); } else { add(p<<1|1, mid+1, r, x, v); } t[p] = min(t[p<<1], t[p<<1|1]); } } } sg1; int rt[N], cnt; struct node{ int ls, rs, sum, pos; ll val; } t[N*60]; inline int add(int p, int l, int r, int x, int v, int ps){ ++ cnt; t[cnt] = t[p]; p = cnt; if(l == r){ ++ t[p].sum; t[p].val += v; t[p].pos = ps; } else { int mid = l + r >> 1; if(x <= mid){ t[p].ls = add(t[p].ls, l, mid, x, v, ps); } else { t[p].rs = add(t[p].rs, mid+1, r, x, v, ps); } t[p].sum = t[t[p].ls].sum + t[t[p].rs].sum; t[p].val = t[t[p].ls].val + t[t[p].rs].val; t[p].pos = max(t[t[p].ls].pos, t[t[p].rs].pos); } return p; } inline int qry(int p, int q, int l, int r, int k){ if(l == r){ return l; } else { int mid = l + r >> 1; int v = t[t[p].rs].sum - t[t[q].rs].sum; if(v >= k){ return qry(t[p].rs, t[q].rs, mid+1, r, k); } else { return qry(t[p].ls, t[q].ls, l, mid, k-v); } } } int tmp; inline ll qvl(int p, int l, int r, int ql, int qr){ if(qr < l || r < ql){ return 0; } else if(ql <= l && r <= qr){ tmp = max(tmp, t[p].pos); return t[p].val; } else { int mid = l + r >> 1; return qvl(t[p].ls, l, mid, ql, qr) + qvl(t[p].rs, mid+1, r, ql, qr); } } void solve(){ read(n, q); for(int i = 1; i <= n; ++ i){ read(a[i]); } for(int i = 1; i <= n; ++ i){ read(b[i]); fb[b[i]] = i; } for(int i = 1; i <= n; ++ i){ read(c[i]); } rt[0] = 1; ++ cnt; for(int i = 1; i <= n; ++ i){ rt[i] = add(rt[i-1], 1, n, b[i], c[i], i); } for(int i = 1; i <= q; ++ i){ int l, r, k; read(l, r, k); if(k == 1){ qrr[++tq] = { l, r, i }; } else { int x = qry(rt[r], rt[l-1], 1, n, k-1); ll val = - qvl(rt[l-1], 1, n, x, n); tmp = 0; val += qvl(rt[r], 1, n, x, n); int ps = tmp; ans[i] += val; int y = qry(rt[ps-1], rt[l-1], 1, n, k-1); if(ps == r){ ans[i] += c[fb[y]]; } else { qr[i] = { fb[y], r, i }; g[ps+1].push_back(i); } } } sg1.build(1, 1, n+1); for(int i = n; i >= 1; -- i){ f[i][0] = sg1.ask(1, 1, n+1, b[i], n+1); sg1.add(1, 1, n+1, a[i], i); for(int j : g[i]){ int y = qr[j].x, r = qr[j].r, id = qr[j].id; int p = sg1.ask(1, 1, n+1, b[y], n+1); if(p > r){ ans[id] += c[y]; } else { qrr[++tq] = { p, r, id }; } } } f[n+1][0] = n+1; for(int i = 1; i < 19; ++ i){ for(int j = 1; j <= n+1; ++ j){ f[j][i] = f[f[j][i-1]][i-1]; } } for(int i = 1; i <= tq; ++ i){ int l = qrr[i].x, r = qrr[i].r; for(int i = 18; i >= 0; -- i){ if(f[l][i] <= r){ l = f[l][i]; } } ans[qrr[i].id] += c[l]; } for(int i = 1; i <= q; ++ i){ println(ans[i]); } }
17. UNR #6 - 面基之路
考虑答案其实等价于所有人走到一个点的最小时间。
分类讨论这个点在点上还是在边上:
- 在点上,直接求出到每个人最短路最大值即可。
- 在边上,求出二元组
表示每个人到边两端最短路,按 升序 降序排列后一定是一个前缀走到点 ,一个后缀走到点 ,贡献为 (注意能贡献当且仅当 )。
点击查看代码
//uoj747 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 1e5 + 10; int n, m, k, a[25]; vector<pair<int, int> > g[N]; ll dis[25][N], ans = 1e18; int eu[N*2], ev[N*2], ew[N*2]; pair<ll, ll> tmp[25]; int vis[N]; void solve(){ scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++ i){ int u, v, w; scanf("%d%d%d", &u, &v, &w); g[u].push_back(make_pair(v, w)); g[v].push_back(make_pair(u, w)); eu[i] = u; ev[i] = v; ew[i] = w; } scanf("%d", &k); ++ k; a[1] = 1; for(int i = 2; i <= k; ++ i){ scanf("%d", &a[i]); } for(int i = 1; i <= k; ++ i){ priority_queue<pair<ll, int> > q; memset(dis[i], 0x3f, sizeof(dis[i])); memset(vis, 0, sizeof(vis)); dis[i][a[i]] = 0; q.push(make_pair(0, a[i])); while(!q.empty()){ int x = q.top().second; q.pop(); if(vis[x]){ continue; } vis[x] = 1; for(auto j : g[x]){ int y = j.first, z = j.second; if(dis[i][y] > dis[i][x] + z){ dis[i][y] = dis[i][x] + z; q.push(make_pair(-dis[i][y], y)); } } } } for(int i = 1; i <= n; ++ i){ ll tmp = 0; for(int j = 1; j <= k; ++ j){ tmp = max(tmp, dis[j][i]); } ans = min(ans, tmp * 2); } for(int i = 1; i <= m; ++ i){ int u = eu[i], v = ev[i], w = ew[i]; for(int j = 1; j <= k; ++ j){ tmp[j] = make_pair(dis[j][u], -dis[j][v]); } sort(tmp + 1, tmp + k + 1); ll mx = 0, my; for(int p = k; p > 1; -- p){ mx = max(mx, -tmp[p].second); my = tmp[p-1].first; if(mx + w > my && my + w > mx){ ans = min(ans, mx + my + w); } } } printf("%lld\n", ans); }
18. UNR #6 - 机器人表演
设
首先考虑对于给定的
定义
转移有:
第一个式子是匹配末尾的两个括号,第二个式子是将末尾的括号不与
那么得到了一个 dp 套 dp 的做法:设
考虑贪心地匹配,设
- 若
,直接匹配; - 否则若
或 ,将 放入括号串中; - 否则
,此时出现失配,预处理一个 使得 ,将 舍弃放入括号串中。
但是 评论回复 hehezhou:我的评价是:歪打正着。证明一下这个失配转移使得括号串依旧合法即可。
点击查看代码
//uoj748 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 310; const ll P = 998244353; int n, t, b[N], pr[N]; char s[N]; int f[2][N*6][N]; void upd(int &x, int y){ x += y; if(x >= P){ x -= P; } } void solve(){ scanf("%d%d%s", &n, &t, s+1); pr[0] = -1; for(int i = 1; i <= n; ++ i){ if(s[i] == '0'){ b[i] = b[i-1] + 1; } else { b[i] = b[i-1] - 1; } pr[i] = -1; for(int j = i-1; j >= 0; -- j){ if(b[j] < b[i]){ pr[i] = j; break; } } } int m = n + 2 * t + 10; f[0][m][0] = 1; for(int i = 1; i <= n + 2 * t; ++ i){ int ii = i&1; for(int j = m-i; j <= m+i; ++ j){ for(int k = 0; k <= n; ++ k){ int val = f[ii^1][j][k]; if(k < n && s[k+1] == '0'){ upd(f[ii][j+1][k+1], val); } else { upd(f[ii][j+1][k], val); } if(k < n && s[k+1] == '1'){ upd(f[ii][j-1][k+1], val); } else if(j - m > b[k]){ upd(f[ii][j-1][k], val); } else if(pr[k] != -1){ upd(f[ii][j-1][pr[k]], val); } f[ii^1][j][k] = 0; } } } printf("%d\n", f[n&1][m+b[n]][n]); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步