##2018冬令營模擬測試賽(十四)html
###[Problem A]primeios
####試題描述 c++
####輸入 見「試題描述」git
####輸出 見「試題描述」測試
####輸入示例 見「試題描述」優化
####輸出示例 見「試題描述」this
####數據規模及約定 見「試題描述」spa
####題解 考慮 $\sum_{i=1}^n { 2^{f(i)} }$ 這個式子的組合意義,咱們發現 $2^{f(i)}$ 的意義就是 $i$ 這個數的每一個不一樣質因子都有選和不選兩種方案,$2^{f(i)}$ 即方案數,也就是說 $i$ 的無平方因子的約數個數。形式化地,即code
$$ 2^{f(i)} = \sum_{d|i} { \mu^2(d) } $$htm
咱們代入到原式當中,獲得
$$ \sum_{i=1}^n { 2^{f(i)} } \\ = \sum_{i=1}^n { \sum_{d|i} { \mu^2(d) } } \\ = \sum_{d=1}^n { \mu^2(d) \sum_{d|i, i \le n} { 1 } } \\ = \sum_{d=1}^n { \mu^2(d) \lfloor \frac{n}{d} \rfloor } $$
然而這時 $\mu^2(d)$ 的前綴和並很差處理,咱們考慮給它變個形。先理解一下 $\mu^2(d)$ 的意義,$\mu^2(d) = 1 \Leftrightarrow d 沒有平方因子 \Leftrightarrow d 的最大徹底平方約數 = 1$,假設 $g(d)$ 就是 $d$ 的最大徹底平方約數,因而上面的條件又等價於 $\sqrt{g(d)} = 1$,因而能夠把式子變成以下形態
$$ \mu^2(d) \\ = [\sqrt{g(d)} = 1] \\ = \sum_{i|\sqrt{g(d)}} { \mu(i) } \\ = \sum_{i^2|g(d)} { \mu(i) } \\ = \sum_{i^2|d} { \mu(i) } $$
而後代入到上式中,獲得
$$ \sum_{d=1}^n { \mu^2(d) \lfloor \frac{n}{d} \rfloor } \\ = \sum_{d=1}^n { \lfloor \frac{n}{d} \rfloor \sum_{i^2|d} { \mu(i) } } \\ = \sum_{i=1}^{\lfloor \sqrt{n} \rfloor} { \mu(i) \sum_{i^2|d, d \le n} { \lfloor \frac{n}{d} \rfloor } } \\ = \sum_{i=1}^{\lfloor \sqrt{n} \rfloor} { \mu(i) \sum_{d=1}^{\lfloor \frac{n}{i^2} \rfloor} { \lfloor \frac{\lfloor \frac{n}{i^2} \rfloor}{d} \rfloor } } $$
因而能夠預處理 $\mu(i)$ 的前綴和,而後分段算後面那坨 sigma,sigma 裏面的也暴力分段算,最後時間複雜度是 $O(\sqrt{n}\log n)$ 的(這個能夠積分一下算出來)。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <cmath> using namespace std; #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++) #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--) #define LL long long LL read() { LL x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 1000010 #define MOD 998244353 #define LL long long bool vis[maxn]; int prime[maxn], cp, mu[maxn], su[maxn]; void init(int n) { mu[1] = su[1] = 1; rep(i, 2, n) { if(!vis[i]) prime[++cp] = i, mu[i] = -1; for(int j = 1; j <= cp && i * prime[j] <= n; j++) { vis[i*prime[j]] = 1; if(i % prime[j] == 0) { mu[i*prime[j]] = 0; break; } mu[i*prime[j]] = -mu[i]; } su[i] = su[i-1] + mu[i]; } return ; } int calc(LL n) { int ans = 0; for(LL i = 1; i <= n; ) { LL r = min(n / (n / i), n); ans += (LL)(r - i + 1) * (n / i) % MOD; if(ans >= MOD) ans -= MOD; i = r + 1; } return ans; } int main() { LL n = read(); int m = sqrt(n + .5); init(m); int ans = 0; for(int i = 1; i <= m; ) { int r = min((int)sqrt(n / (n / ((LL)i * i)) + .5), m); ans += ((LL)(su[r] - su[i-1]) * calc(n / ((LL)i * i)) % MOD + MOD) % MOD; if(ans >= MOD) ans -= MOD; i = r + 1; } printf("%d\n", ans); return 0; }
###[Problem B]final
####試題描述
####輸入 見「試題描述」
####輸出 見「試題描述」
####輸入示例 見「試題描述」
####輸出示例 見「試題描述」
####數據規模及約定 見「試題描述」
補充:$1 \le m \le 3n$。
####題解 這題和某道我以前作過的題(那場比賽的 C 題)作法同樣,咱們發現每一個轉移都是平移而後疊加上去,因此能夠當作多項式乘上 $x^t$,而後將全部狀態、轉移都轉化成點值就能夠直接作矩陣快速冪了。注意這題不須要循環卷積,直接開成 $3n$ 大小 FFT 就行了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++) #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--) int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 2510 #define maxm 8200 #define MOD 998244353 #define Groot 3 #define LL long long int A[3], all, cbit[8], trans[8][8]; bool Can(int s) { rep(i, 0, 1) if((s >> i & 1) && (s >> i + 1 & 1) && ((A[1] & 1) || (A[1] >> 2 & 1))) return 0; return 1; } int Pow(int a, int b) { int ans = 1, t = a; while(b) { if(b & 1) ans = (LL)ans * t % MOD; t = (LL)t * t % MOD; b >>= 1; } return ans; } int N, brev[maxm]; void FFT(int *a, int len, int tp) { int n = 1 << len; rep(i, 0, n - 1) if(i < brev[i]) swap(a[i], a[brev[i]]); rep(i, 1, len) { int wn = Pow(Groot, MOD - 1 >> i); if(tp < 0) wn = Pow(wn, MOD - 2); for(int j = 0; j < n; j += 1 << i) { int w = 1; rep(k, 0, (1 << i >> 1) - 1) { int la = a[j+k], ra = (LL)w * a[j+k+(1<<i>>1)] % MOD; a[j+k] = (la + ra) % MOD; a[j+k+(1<<i>>1)] = (la - ra + MOD) % MOD; w = (LL)w * wn % MOD; } } } if(tp < 0) rep(i, 0, n - 1) a[i] = (LL)a[i] * Pow(n, MOD - 2) % MOD; return ; } void prod(int *a, const int *b, const int *c) { rep(i, 0, N - 1) { a[i] += (LL)b[i] * c[i] % MOD; if(a[i] >= MOD) a[i] -= MOD; } return ; } struct Matrix { int A[8][8][maxm], n, m; Matrix() {} Matrix(int _, int __): n(_), m(__) {} Matrix operator = (const Matrix& t) { n = t.n; m = t.m; rep(i, 0, n) rep(j, 0, m) rep(k, 0, N - 1) A[i][j][k] = t.A[i][j][k]; return *this; } Matrix operator * (const Matrix& t) const { Matrix ans(n, t.m); rep(i, 0, ans.n) rep(j, 0, ans.m) { int *a = ans.A[i][j]; rep(k, 0, N - 1) a[k] = 0; rep(k, 0, m) prod(a, A[i][k], t.A[k][j]); } return ans; } Matrix operator *= (const Matrix& t) { *this = *this * t; return *this; } } base(7, 0), tr(7, 7); Matrix PowM(Matrix& a, int b) { Matrix ans = a, t = a; b--; while(b) { if(b & 1) ans *= t; t *= t; b >>= 1; } return ans; } int main() { int n = read(), m = read(); rep(i, 0, 2) rep(j, 0, 2) A[i] |= (read() << j); all = 7; memset(trans, -1, sizeof(trans)); rep(i, 1, all) cbit[i] = cbit[i-(i&-i)] + 1; rep(s, 0, all) if(Can(s)) { int ban = 0; rep(i, 0, 2) if(s >> i & 1) ban |= i < 2 ? (A[2] >> 1 - i) : (A[2] << 1); rep(ts, 0, all) if(Can(ts) && !(ban & ts)) { int tb = 0; rep(i, 0, 2) if(ts >> i & 1) tb |= i < 2 ? (A[0] >> 1 - i) : (A[0] << 1); if(tb & s) continue; trans[ts][s] = cbit[ts]; // printf("%d -> %d %d\n", s, ts, trans[ts][s]); } } N = 1; int len = 0; while(N <= 3 * n) N <<= 1, len++; rep(i, 0, N - 1) brev[i] = (brev[i>>1] >> 1) | ((i & 1) << len >> 1); memset(base.A, 0, sizeof(base.A)); base.A[0][0][0] = 1; FFT(base.A[0][0], len, 1); memset(tr.A, 0, sizeof(tr.A)); rep(ts, 0, all) rep(s, 0, all) { if(trans[ts][s] >= 0) tr.A[ts][s][trans[ts][s]] = 1; FFT(tr.A[ts][s], len, 1); // printf("tr[%d][%d]: ", ts, s); rep(i, 0, N - 1) printf("%d%c", tr.A[ts][s][i], i < N - 1 ? ' ' : '\n'); } base = PowM(tr, n) * base; int ans = 0; rep(s, 0, all) { FFT(base.A[s][0], len, -1); // printf("%d: ", s); rep(i, 0, N - 1) printf("%d%c", base.A[s][0][i], i < N - 1 ? ' ' : '\n'); ans += base.A[s][0][m]; if(ans >= MOD) ans -= MOD; } printf("%d\n", ans); return 0; }
###[Problem C]gift
####試題描述
####輸入 見「試題描述」
####輸出 見「試題描述」
####輸入示例 見「試題描述」
####輸出示例 見「試題描述」
####數據規模及約定 見「試題描述」
這題出鍋啦,答案下取整改爲上取整減 $1$。
####題解 看到分數就分數規劃(二分),如今就是判斷是否存在方案使得 $\frac{B}{A} > x$,移項獲得 $B - Ax > 0$,因而最大化 $B - Ax$,咱們先將全部的好感度都選上,而後就有兩種代價,一個是犧牲某個好感度,一個是買某個物品,因而這就是經典的最小割模型了。
用最大權閉合子圖的模型會有 $n + m + 2$ 個點,就 T 飛了,要優化。每條邊能夠用方程算一下定多少流量,這樣就只有 $n + 2$ 個點了,而後把重邊縮掉卡卡常數就 A 了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <map> using namespace std; #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++) #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--) const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 204010 #define maxm 416010 #define oo 2147483647 struct Edge { int from, to, flow; Edge() {} Edge(int _1, int _2, int _3): from(_1), to(_2), flow(_3) {} }; struct Dinic { int s, t, n, m, head[maxn], nxt[maxm]; Edge es[maxm]; int vis[maxn], Q[maxn], hd, tl; int cur[maxn]; void init() { m = 0; memset(head, -1, sizeof(head)); return ; } void setn(int _) { n = _; return ; } void AddEdge(int a, int b, int c, int revc = 0) { es[m] = Edge(a, b, c); nxt[m] = head[a]; head[a] = m++; es[m] = Edge(b, a, revc); nxt[m] = head[b]; head[b] = m++; return ; } bool BFS() { memset(vis, 0, sizeof(vis)); vis[t] = 1; hd = tl = 0; Q[++tl] = t; while(hd < tl) { int u = Q[++hd]; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i^1]; if(!vis[e.from] && e.flow) vis[e.from] = vis[u] + 1, Q[++tl] = e.from; } } return vis[s] > 1; } int DFS(int u, int a) { if(u == t || !a) return a; int flow = 0, f; for(int& i = cur[u]; i != -1; i = nxt[i]) { Edge& e = es[i]; if(vis[e.to] == vis[u] - 1 && (f = DFS(e.to, min(a, e.flow)))) { flow += f; a -= f; e.flow -= f; es[i^1].flow += f; if(!a) return flow; } } return flow; } int MaxFlow(int _s, int _t) { s = _s; t = _t; int flow = 0; while(BFS()) { rep(i, 1, n) cur[i] = head[i]; flow += DFS(s, oo); } return flow; } } sol; #define maxgift 4010 #define maxcond 200010 #define pii pair <int, int> #define x first #define y second #define mp(x, y) make_pair(x, y) int CntP; struct Point { int id; Point(): id(0) {} int p() { return id ? id : id = ++ CntP; } } gift[maxgift], S, T; int n, m, sum, mn, price[maxgift], gid[maxgift], half[maxcond], val[maxcond], deg[maxgift]; map <pii, int> cond; bool check(int x) { for(int i = 0; i < sol.m; i += 2) sol.es[i].flow += sol.es[i^1].flow, sol.es[i^1].flow = 0; rep(i, 1, n) sol.es[gid[i]].flow = price[i] * x; rep(i, 1, m) if(half[i] >= 0) sol.es[half[i]].flow = sol.es[half[i]^1].flow = val[i]; int flow = sol.MaxFlow(S.p(), T.p()); return sum - flow > 0; } int main() { n = read(); m = read(); sum = 0; mn = oo; sol.init(); rep(i, 1, n) gid[i] = sol.m, sol.AddEdge(S.p(), gift[i].p(), price[i] = read() << 1), mn = min(mn, price[i]); memset(half, -1, sizeof(half)); rep(i, 1, m) { int a = read(), b = read(); int now = read(); if(a > b) swap(a, b); if(!cond.count(mp(a, b))) cond[mp(a,b)] = i; val[cond[mp(a,b)]] += now; deg[a] += now; deg[b] += now; sum += now << 1; } for(map <pii, int> :: iterator i = cond.begin(); i != cond.end(); i++) half[i->y] = sol.m, sol.AddEdge(gift[i->x.x].p(), gift[i->x.y].p(), val[i->y], val[i->y]); rep(i, 1, n) sol.AddEdge(gift[i].p(), T.p(), deg[i]); sol.setn(CntP); int l = 0, r = sum / mn + 1; while(r - l > 1) { int mid = l + r >> 1; if(check(mid)) l = mid; else r = mid; } printf("%d\n", l); return 0; }