2018冬令營模擬測試賽(十四)

##2018冬令營模擬測試賽(十四)html

###[Problem A]primeios

####試題描述 T_Tc++

Q_Q

####輸入 見「試題描述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

####試題描述 TwT

QwQ

####輸入 見「試題描述

####輸出 見「試題描述

####輸入示例 見「試題描述

####輸出示例 見「試題描述

####數據規模及約定 見「試題描述

補充:$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

####試題描述 TAT

QAQ

####輸入 見「試題描述

####輸出 見「試題描述

####輸入示例 見「試題描述

####輸出示例 見「試題描述

####數據規模及約定 見「試題描述

這題出鍋啦,答案下取整改爲上取整減 $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;
}
相關文章
相關標籤/搜索