最近還沒想好報THUSC仍是PKUSC,THU發個人三類約(再來一瓶)不知道要不要用,甚至不知道營還辦不辦,協議還有沒有用。因此這些事情就暫時先無論了,PKU的題仍是不錯的,就刷一刷划水。由於比較簡單,因此就不單獨寫博客了。c++
數據結構題,兩個 \(\log\) 直接啓發式合併,一個 \(\log\) 須要轉移的時候多維護一些東西。git
對於每一個節點維護一下選擇其子樹裏每一個葉子的權值的機率,線段樹合併轉移便可。算法
/*program by mangoyang*/ #include <bits/stdc++.h> #define inf (0x7f7f7f7f) #define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) typedef long long ll; using namespace std; template <class T> inline void read(T &x){ int ch = 0, f = 0; x = 0; for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; if(f) x = -x; } const int M = 1e9, N = 300005, mod = 998244353; int a[N], rt[N], P, n, ans; vector<int> g[N]; vector<pair<int, int> > Ans; namespace Seg{ #define mid ((l + r) >> 1) int s[N*30], tag[N*30], lc[N*30], rc[N*30], size; inline void init(){ for(int i = 1; i < N * 30; i++) tag[i] = 1; } inline void pushdown(int u){ if(tag[u] == 1) return; if(lc[u]){ s[lc[u]] = 1ll * tag[u] * s[lc[u]] % mod; tag[lc[u]] = 1ll * tag[lc[u]] * tag[u] % mod; } if(rc[u]){ s[rc[u]] = 1ll * tag[u] * s[rc[u]] % mod; tag[rc[u]] = 1ll * tag[rc[u]] * tag[u] % mod; } tag[u] = 1; } inline void insert(int &u, int l, int r, int pos){ if(!u) u = ++size; if(l == r) return (void) (s[u] = 1); if(pos <= mid) insert(lc[u], l, mid, pos); else insert(rc[u], mid + 1, r, pos); s[u] = s[lc[u]] + s[rc[u]]; } inline int merge(int x, int y, int sumx, int sumy){ if(!x){ if(!sumx) return y; s[y] = 1ll * s[y] * sumx % mod; tag[y] = 1ll * tag[y] * sumx % mod; return y; } if(!y){ if(!sumy) return x; s[x] = 1ll * s[x] * sumy % mod; tag[x] = 1ll * tag[x] * sumy % mod; return x; } pushdown(x), pushdown(y); int o = ++size; int Lx = (sumx + 1ll * P * s[lc[x]] % mod) % mod; int Ly = (sumy + 1ll * P * s[lc[y]] % mod) % mod; int Rx = (sumx + 1ll * (mod + 1 - P) * s[rc[x]] % mod) % mod; int Ry = (sumy + 1ll * (mod + 1 - P) * s[rc[y]] % mod) % mod; lc[o] = merge(lc[x], lc[y], Rx, Ry); rc[o] = merge(rc[x], rc[y], Lx, Ly); s[o] = (s[lc[o]] + s[rc[o]]) % mod; return o; } inline void getans(int u, int l, int r){ if(l == r) return (void) Ans.push_back(make_pair(l, s[u])); pushdown(u); if(lc[u]) getans(lc[u], l, mid); if(rc[u]) getans(rc[u], mid + 1, r); } } inline int Pow(int a, int b){ int ans = 1; for(; b; b >>= 1, a = 1ll * a * a % mod) if(b & 1) ans = 1ll * ans * a % mod; return ans; } inline void dfs(int u){ if(!(int) g[u].size()) Seg::insert(rt[u], 1, M, a[u]); for(int i = 0; i < (int) g[u].size(); i++){ dfs(g[u][i]); P = a[u]; rt[u] = Seg::merge(rt[u], rt[g[u][i]], 0, 0); } } int main(){ read(n); for(int i = 1, x; i <= n; i++) read(x), g[x].push_back(i); for(int i = 1; i <= n; i++){ read(a[i]); if((int) g[i].size()) a[i] = 1ll * a[i] * Pow(10000, mod1- 2) % mod; } Seg::init(); dfs(1), Seg::getans(rt[1], 1, M), sort(Ans.begin(), Ans.end()); for(int i = 0; i < (int) Ans.size(); i++){ (ans += 1ll * (i + 1) * Ans[i].first % mod * Ans[i].second % mod * Ans[i].second % mod) %= mod; } cout << ans << endl; return 0; }
一個顯然的貪心策略是從大到小盡量打出強化牌(至多 \(k-1\) 張),而後從大到小盡量打出攻擊牌。數據結構
能夠分別對兩種牌排序而後按照這個策略設計 dp。spa
\(f[i][j]\) 表示前 \(i\) 張強化牌摸了 \(j\) 張的指望加成,從大到小對強化牌排序,枚舉當前這張牌是否被摸到來轉移。設計
\(g[i][j]\) 表示前 \(i\) 張攻擊牌摸了 \(j\) 張的指望攻擊力之和,\(j\) 越大能夠打的攻擊牌越多,因此從小到大排序來轉移。code
/*program by mangoyang*/ #include <bits/stdc++.h> #define inf (0x7f7f7f7f) #define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) typedef long long ll; using namespace std; template <class T> inline void read(T &x){ int ch = 0, f = 0; x = 0; for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; if(f) x = -x; } const int N = 3005, mod = 998244353; int a[N], b[N], js[N], inv[N], f[2][N], g[2][N], n, m, k; inline void up(int &x, int y){ x = (x + y >= mod ? x + y - mod : x + y); } inline int Pow(int a, int b){ int ans = 1; for(; b; b >>= 1, a = 1ll * a * a % mod) if(b & 1) ans = 1ll * ans * a % mod; return ans; } inline int C(int x, int y){ return 1ll * js[x] * inv[y] % mod * inv[x-y] % mod; } inline void solve(){ read(n), read(m), read(k); for(int i = 1; i <= n; i++) read(a[i]); for(int i = 1; i <= n; i++) read(b[i]); sort(a + 1, a + n + 1, greater<int>()); sort(b + 1, b + n + 1); for(int i = 0; i <= n; i++) f[0][i] = f[1][i] = g[0][i] = g[1][i] = 0; f[0][0] = 1; for(int i = 1, o = 1; i <= n; i++, o ^= 1){ for(int j = 0; j <= i; j++){ f[o][j] = f[o^1][j]; if(j && j < k) up(f[o][j], 1ll * a[i] * f[o^1][j-1] % mod); else if(j) up(f[o][j], f[o^1][j-1]); } } for(int i = 1, o = 1; i <= n; i++, o ^= 1){ for(int j = 0; j <= i; j++){ g[o][j] = g[o^1][j]; if(j){ if(m - j < k - 1) up(g[o][j], g[o^1][j-1]); up(g[o][j], 1ll * C(i - 1, j - 1) * b[i] % mod); } } } int ans = 0; for(int i = 0; i <= min(m, n); i++) if(m - i <= n) up(ans, 1ll * f[n&1][i] * g[n&1][m-i] % mod); printf("%d\n", ans); } int main(){ js[0] = inv[0] = 1; for(int i = 1; i < N; i++) js[i] = 1ll * js[i-1] * i % mod, inv[i] = Pow(js[i], mod - 2); int T; read(T); while(T--) solve(); return 0; }
直接狀壓 \(dp\) 是 \(O(3^nn)\) 的,實際上選了會衝突的點與已經選了的點顯然能夠看作同一類點,而後就 \(O(2^nn)\) 了。排序
/*program by mangoyang*/ #include<bits/stdc++.h> #define inf (0x7f7f7f7f) #define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) typedef long long ll; using namespace std; template <class T> inline void read(T &x){ int ch = 0, f = 0; x = 0; for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; if(f) x = -x; } const int mod = 998244353; int cnt[1<<21], dp[1<<21][21], js[21], inv[21], ed[21], n, m; inline int Pow(int a, int b){ int ans = 1; for(; b; b >>= 1, a = 1ll * a * a % mod) if(b & 1) ans = 1ll * ans * a % mod; return ans; } inline int A(int x, int y){ return 1ll * js[x] * inv[x-y] % mod; } inline void up(int &x, int y){ x = (x + y >= mod ? x + y - mod : x + y); } int main(){ read(n), read(m); js[0] = inv[0] = 1; for(int i = 1; i <= n; i++){ js[i] = 1ll * js[i-1] * i % mod; inv[i] = Pow(js[i], mod - 2); } for(int i = 1, x, y; i <= m; i++){ read(x), read(y); ed[x] |= 1 << (y - 1); ed[y] |= 1 << (x - 1); } for(int s = 0; s < (1 << n); s++) for(int i = 0; i < n; i++) if((1 << i) & s) cnt[s]++; dp[0][0] = 1; for(int s = 0; s < (1 << n) - 1; s++) for(int i = 0; i < n; i++) if(dp[s][i]){ for(int j = 0; j < n; j++) if(!((1 << j) & s)){ int t = s | (1 << j) | ed[j+1]; up(dp[t][i+1], 1ll * dp[s][i] * A(n - cnt[s] - 1, cnt[s^t] - 1) % mod); } } int mx = 0; for(int i = 1; i <= n; i++) if(dp[(1<<n)-1][i]) mx = i; cout << 1ll * dp[(1<<n)-1][mx] * inv[n] % mod << endl; return 0; }
設 \(dp[x][s]\) 爲從 \(x\) 出發走到點集 \(s\) 中任意一個點的指望步數,而後 \(\min-\max\) 容斥便可 ,直接分層圖+高斯消元跑不過去,能夠用小套路把未知數設出來之後每一個點均可以用其父親的答案乘上一個係數再加上一個常數來表示,而後跑一遍樹形dp就作完了。
遊戲
/*program by mangoyang*/ #include<bits/stdc++.h> #define inf (0x7f7f7f7f) #define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) typedef long long ll; using namespace std; template <class T> inline void read(T &x){ int ch = 0, f = 0; x = 0; for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; if(f) x = -x; } const int N = 1 << 21, mod = 998244353; vector<int> g[N]; int mn[N], cnt[N], in[N], A[N], B[N], n, q, rt; inline int Pow(int a, int b){ int ans = 1; for(; b; b >>= 1, a = 1ll * a * a % mod) if(b & 1) ans = 1ll * ans * a % mod; return ans; } inline void dfs(int u, int fa){ if(in[u]) return (void) (A[u] = B[u] = 0); int SA = 0, SB = 0, deg = (int) g[u].size(); for(int i = 0; i < (int) g[u].size(); i++){ int v = g[u][i]; if(v == fa) continue; dfs(v, u); SA = (SA + A[v]) % mod, SB = (SB + B[v]) % mod; } deg = Pow(deg, mod - 2); int x = (1 - 1ll * SA * deg % mod + mod) % mod; x = Pow(x, mod - 2); A[u] = 1ll * deg * x % mod; B[u] = 1ll * x * (1ll * SB * deg % mod + 1) % mod; } int main(){ read(n), read(q), read(rt); for(int i = 1, x, y; i < n; i++){ read(x), read(y); g[x].push_back(y), g[y].push_back(x); } for(int s = 0; s < (1 << n); s++){ for(int i = 1; i <= n; i++){ in[i] = ((1 << (i - 1)) & s) ? 1 : 0; cnt[s] += in[i]; } dfs(rt, 0), mn[s] = B[rt]; } while(q--){ int num, sta = 0; read(num); for(int i = 1, x; i <= num; i++) read(x), sta |= 1 << (x - 1); int ans = 0; for(int s = sta; s; s = (s - 1) & sta) (ans += 1ll * ((cnt[s] & 1) ? 1 : mod - 1) * mn[s] % mod) %= mod; printf("%d\n", ans); } return 0; }
對於每一個點枚舉一下要不要,統計一下會影響的人數,組合數算一下就行了get
/*program by mangoyang*/ #include <bits/stdc++.h> #define inf (0x7f7f7f7f) #define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) typedef long long ll; using namespace std; template <class T> inline void read(T &x){ int ch = 0, f = 0; x = 0; for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; if(f) x = -x; } #define fi first #define se second const int N = 1000005, mod = 998244353; pair<int, int> a[N]; int js[N], inv[N], ans[N], n, k; inline int Pow(int a, int b){ int ans = 1; for(; b; b >>= 1, a = 1ll * a * a % mod) if(b & 1) ans = 1ll * ans * a % mod; return ans; } inline int C(int x, int y){ if(x < y || x < 0 || y < 0) return 0; return 1ll * js[x] * inv[y] % mod * inv[x-y] % mod; } int main(){ read(n), read(k); js[0] = inv[0] = 1; for(int i = 1; i <= n; i++){ js[i] = 1ll * js[i-1] * i % mod; inv[i] = Pow(js[i], mod - 2); read(a[i].fi), a[i].se = i; } sort(a + 1, a + n + 1); int p = 0, q = 0; for(int i = 1; i <= n; i++){ if(a[i].fi == a[i-1].fi && i > 1){ ans[a[i].se] = ans[a[i-1].se]; continue; } while(a[p+1].fi * 2 < a[i].fi && p < n) p++; while(a[q+1].fi < a[i].fi * 2 && q < n) q++; int tot1 = i - p - 1, tot2 = max(q, i) - i + 1; (ans[a[i].se] += C(n - tot1 - 1, k)) %= mod; (ans[a[i].se] += C(n - tot2, k - tot2)) %= mod; } for(int i = 1; i <= n; i++) printf("%d\n", ans[i]); return 0; }
考慮每個集合成爲最大前綴和的方案數,這個等於其內部排列知足所有選是最大前綴和乘上剩餘元素不管怎麼排都不存在一個前綴和 \(>0\) 的方案數,兩部分分開來狀壓 \(dp\) 便可。
/*program by mangoyang*/ #include <bits/stdc++.h> #define inf (0x7f7f7f7f) #define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) typedef long long ll; using namespace std; template <class T> inline void read(T &x){ int ch = 0, f = 0; x = 0; for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; if(f) x = -x; } const int N = (1 << 20), mod = 998244353; int a[N], f[N], sum[N], g[N], n; inline void up(int &x, int y){ x = x + y >= mod ? x + y - mod : x + y; } int main(){ read(n); for(int i = 1; i <= n; i++) read(a[i]); for(int s = 1; s < (1 << n); s++) for(int i = 0; i < n; i++) if((1 << i) & s) sum[s] += a[i+1]; g[0] = 1; for(int i = 0; i < n; i++) f[1<<i] = 1; for(int s = 0; s < (1 << n); s++) for(int i = 0; i < n; i++) if(!((1 << i) & s)){ int t = s ^ (1 << i); if(sum[s] > 0) up(f[t], f[s]); if(sum[t] <= 0) up(g[t], g[s]); } int ans = 0; for(int s = 0; s < (1 << n); s++){ sum[s] = (sum[s] % mod + mod) % mod; up(ans, 1ll * sum[s] * f[s] % mod * g[((1<<n)-1)^s] % mod); } cout << ans << endl; return 0; }
能夠發現每一個點出發最多會向右走一步,因而倍增出每一個點走 \(2^j\) 步能走到的最左端點,已經走到這之間全部點的距離和,每次詢問拆成兩個以 \(x\) 結尾的後綴來詢問便可。
/*program by mangoyang*/ #include <bits/stdc++.h> #define inf (0x7f7f7f7f) #define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) typedef long long ll; using namespace std; template <class T> inline void read(T &x){ int ch = 0, f = 0; x = 0; for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; if(f) x = -x; } const int N = 300005; ll a[N], f[N][21], s[N][21]; int n, q; inline ll calc(int x, int L){ if(a[x] <= L) return x - L; ll ans = x - a[x], now = 1; x = a[x]; for(int i = 20; ~i; i--) if(f[x][i] >= L){ ans += s[x][i] + (x - f[x][i]) * now; now += 1 << i, x = f[x][i]; } return ans + (x - L) * (now + 1); } int main(){ read(n), a[1] = 1; for(int i = 2; i <= n; i++) read(a[i]); for(int i = n; i >= 1; i--){ f[i][0] = min(a[i], i == n ? inf : f[i+1][0]); s[i][0] = i - f[i][0]; } for(int j = 1; j <= 20; j++) for(int i = 1; i <= n; i++){ f[i][j] = f[f[i][j-1]][j-1]; s[i][j] = s[i][j-1] + s[f[i][j-1]][j-1]; s[i][j] += (f[i][j-1] - f[f[i][j-1]][j-1]) << (j - 1); } read(q); for(int i = 1, l, r, x; i <= q; i++){ read(l), read(r), read(x); ll ans = calc(x, l) - calc(x, r + 1); ll g = __gcd(ans, (ll) r - l + 1); printf("%lld/%lld\n", ans / g, (r - l + 1) / g); } return 0; }
border有關的計數題能夠從等差數列和週期兩個角度考慮,這個題顯然不能等差數列因而就直接上週期定理。
套用週期定理獲得若是 \(i\) 是一個 border,那麼全部模 \(|S|-i\) 同餘的位置必須相同,也就是不能出現兩個距離爲 \(|S|-i-1\) 的 \(0,1\) 位置,直接用 FTT 統計全部差的信息便可。
/*program by mangoyang*/ #include <bits/stdc++.h> #define inf (0x7f7f7f7f) #define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) typedef long long ll; using namespace std; template <class T> inline void read(T &x){ int ch = 0, f = 0; x = 0; for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; if(f) x = -x; } const int N = (1 << 22), P = 998244353, G = 3; char s[N]; int a[N], b[N]; namespace poly{ int rev[N], lg, len; inline int Pow(int a, int b){ int ans = 1; for(; b; b >>= 1, a = 1ll * a * a % P) if(b & 1) ans = 1ll * ans * a % P; return ans; } inline void timesinit(int lenth){ for(len = 1, lg = 0; len <= lenth; len <<= 1, lg++); for(int i = 0; i < len; i++) rev[i] = (rev[i>>1] >> 1) | ((i & 1) << (lg - 1)); } inline void dft(int *a, int sgn){ for(int i = 0; i < len; i++) if(i < rev[i]) swap(a[i], a[rev[i]]); for(int k = 2; k <= len; k <<= 1){ int w = Pow(G, (P - 1) / k); if(sgn == - 1) w = Pow(w, P - 2); for(int i = 0; i < len; i += k){ int now = 1; for(int j = i; j < i + (k >> 1); j++){ int x = a[j], y = 1ll * a[j+(k>>1)] * now % P; a[j] = x + y >= P ? x + y - P : x + y; a[j+(k>>1)] = x - y < 0 ? x - y + P : x - y; now = 1ll * now * w % P; } } } if(sgn == -1){ int Inv = Pow(len, P - 2); for(int i = 0; i < len; i++) a[i] = 1ll * a[i] * Inv % P; } } } using poly::timesinit; using poly::dft; int main(){ scanf("%s", s); int n = strlen(s); for(int i = 0; i < n; i++) a[i] = s[i] == '0', b[i] = s[i] == '1'; reverse(b, b + n); timesinit(n + n - 1); dft(a, 1), dft(b, 1); for(int i = 0; i < poly::len; i++) a[i] = 1ll * a[i] * b[i] % P; dft(a, -1); ll ans = 1ll * n * n; for(int i = 1; i < n; i++){ int flag = 0; for(int j = i; j < n; j += i) if(a[n-1+j] || a[n-1-j]) flag = 1; if(!flag) ans ^= 1ll * (n - i) * (n - i); } cout << ans << endl; return 0; }