2018冬令营模拟测试赛(十四)

2018冬令营模拟测试赛(十四)

[Problem A]prime

试题描述

T_T

Q_Q

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

见“试题描述

题解

考虑 \(\sum_{i=1}^n { 2^{f(i)} }\) 这个式子的组合意义,我们发现 \(2^{f(i)}\) 的意义就是 \(i\) 这个数的每个不同质因子都有选和不选两种方案,\(2^{f(i)}\) 即方案数,也就是说 \(i\) 的无平方因子的约数个数。形式化地,即

\[2^{f(i)} = \sum_{d|i} { \mu^2(d) } \]

我们代入到原式当中,得到

\[\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;
}
posted @ 2018-01-13 11:06  xjr01  阅读(296)  评论(0编辑  收藏  举报