2023武汉联训衡水版

2023省选武汉联测9

A. 背包问题模板

首先二进制分组,注意剩余部分也拆分成 \(\sum 2^i\) 的形式

然后搞一个所谓二进制数位背包 \(DP\)

可以看这个题 https://www.luogu.com.cn/problem/P3188

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef __int128_t LLL;
typedef pair<int, LLL> piL;
ll read(){
	ll x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
ll n, m;
LLL f[64][805], g[64][805];
vector<piL>v[64];
int p[105], top;
void print(LLL ans){
	while(ans)p[++top] = ans % 10, ans /= 10;
	if(!top)p[++top] = 0;
	for(int i = top; i >= 1; --i)putchar('0' + p[i]); putchar('\n');
}
int main(){
	freopen("bag.in","r",stdin);
	freopen("bag.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i){
		ll a = read(), b = read(), c = read();
		for(int j = 0; ; ++j){
			ll val = (1ll << j);
			if(val > a)break;
			v[j].push_back(piL(b, (LLL)val * c));
			a -= val;
		}
		for(int j = 0; a; ++j)if((a >> j) & 1){
			a ^= (1ll << j); 
			v[j].push_back(piL(b, (LLL)(1ll << j) * c));
		}
	}
	m = read();
	for(int i = 0; i <= __lg(m); ++i){
		for(piL now : v[i]){
			for(int j = 800; j >= now.first; --j)
				g[i][j] = max(g[i][j], g[i][j - now.first] + now.second);
		}
	}
	for(int i = 1; i <= 800; ++i)f[0][i] = g[0][i];
	for(int i = 1; i <= __lg(m); ++i){
		int s = (m >> (i - 1)) & 1;
		for(int j = 0; j <= 800; ++j)
			for(int k = 0; k <= j; ++k)
				f[i][j] = max(f[i][j], f[i - 1][((j - k) << 1) + s] + g[i][k]);
	}
	print(f[__lg(m)][1]);
	return 0;
}

B. 南河泄露

\(f_{l, r, 1 / 0}\) 表示在区间 \([l, r]\) 在左/右端点的贡献

枚举选择点 \(k\)

\[ f_{l, r, 1 / 0} = max_{k}(min(f_{l, k, 0}, f_{k + 1, r, 1}) + d_k + (s[l / r] - s[k]) ^ 2) \]

$ f_{l, r, 1 / 0} $ 在 \(l / r\) 一端固定时随着另一端单调

于是可以双指针得到 \(k\)

现在的问题是如何取 \(max\)

一侧可以直接取,另一侧可以斜率优化

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

ll read(){
	ll x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 3005;
int n, s[maxn];
ll d[maxn], f[maxn][maxn][2], rmx[maxn];
ll sq(ll x){return x * x;}
int pr[maxn];
struct tb{
	int x[maxn], top; ll y[maxn];
	void push_l(int nx, ll ny){
		while(top > 1 && (y[top] - ny) * (x[top - 1] - x[top]) <= (y[top - 1] - y[top]) * (x[top] - nx))--top;
		x[++top] = nx; y[top] = ny;
	}
	void push_r(int nx, ll ny){
		while(top > 1 && (y[top] - y[top - 1]) * (nx - x[top]) <= (x[top] - x[top - 1]) * (ny - y[top]))--top;
		x[++top] = nx; y[top] = ny;
	}
	ll query(int k){
		if(top == 0)return -0x3f3f3f3f3f3f3f3f;
		while(top > 1 && y[top] - 1ll * k * x[top] < y[top - 1] - 1ll * k * x[top - 1])--top;
        return y[top] - 1ll * x[top] * k;
	}
	void clear(){top = 0;}
}tmp, R[maxn];
int main(){
	freopen("leak.in","r",stdin);
	freopen("leak.out","w",stdout);
	n = read(); 
	for(int i = 1; i <= n; ++i)s[i] = read();
	for(int i = 1; i <= n; ++i)d[i] = read();
	for(int l = n + 1; l >= 1; --l){
		pr[l] = l - 1; tmp.clear();
		int p = l; ll mx = 0;
		for(int r = l; r <= n + 1; ++r){
			while(p <= r - 1 && f[l][p][1] < f[p + 1][r][0]){
				mx = max(mx, f[l][p][1] + sq(s[p] - s[l - 1]) + d[p]);
				tmp.push_r(2 * s[p], f[l][p][1] + sq(s[p]) + d[p]);
				++p;
			}
			while(pr[r] >= p){
				rmx[r] = max(rmx[r], f[pr[r] + 1][r][0] + 1ll * sq(s[r] - s[pr[r]]) + d[pr[r]]);
				R[r].push_l(2 * s[pr[r]], f[pr[r] + 1][r][0] + sq(s[pr[r]]) + d[pr[r]]);
				--pr[r];
			}
			f[l][r][0] = max(mx, R[r].query(s[l - 1]) + sq(s[l - 1]));
			f[l][r][1] = max(rmx[r], tmp.query(s[r]) + sq(s[r]));
		}
	}
	printf("%lld\n",f[1][n + 1][0]);
	return 0;
}

C. 最短路问题模板

虽然但是,直接用类似倍增的方法优化建图就能过,常数更小的是树剖+线段树优化

正解咕了

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pli;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 5e5 + 55, mx = 18;
struct edge{int to, net, val;}e[maxn * 30];
int n, m, head[maxn * 30], tot;
priority_queue<pli, vector<pli>, greater<pli>>q;
ll dis[maxn * 30]; bool vis[maxn * 30];
vector<int>g[maxn];
int cnt, id[maxn][mx], fa[maxn][mx], sub[maxn], dep[maxn];
void add(int u, int v, int w){
	if(!u || !v)return;
	e[++tot] = {v, head[u], w};
	head[u] = tot;
}
void dfs(int x){
	for(int i = 1; i < mx; ++i)fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for(int i = 1; i < mx; ++i)id[x][i] = ++cnt, add(id[x][i], id[x][i - 1], 0), add(id[x][i], id[fa[x][i - 1]][i - 1], 0);
	sub[x] = ++cnt; add(sub[x], x, 0);
	for(int v : g[x])if(v != fa[x][0]){
		fa[v][0] = x; id[v][0] = x; dep[v] = dep[x] + 1;
		dfs(v); add(sub[x], sub[v], 0); 
	}
}
void link(int x){
	int u = read(), v = read(), w = read();
	add(x, u, w); add(x, v, w);
	if(dep[u] < dep[v])swap(u, v);
	for(int i = mx - 1; i >= 0; --i)if(dep[u] - dep[v] >= (1 << i))add(x, id[u][i], w), u = fa[u][i];
	if(u == v)return;
	for(int i = mx - 1; i >= 0; --i)if(fa[u][i] != fa[v][i])add(x, id[u][i], w), add(x, id[v][i], w), u = fa[u][i], v = fa[v][i];
	u = fa[u][0]; add(x, u, w); return;
}
int main(){
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	cnt = n = read(), m = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read(), w = read();
		g[u].push_back(v); g[v].push_back(u);
		add(u, v, w); add(v, u, w);
	}
	dfs(1);
	for(int i = 1; i <= m; ++i){
		int op = read(), x = read();
		if(op & 1)link(x);
		else{int y = read(); add(x, sub[y], read());}
	}
	memset(dis, 0x3f, sizeof(dis)); dis[1] = 0; q.push(pli(0, 1));
	while(!q.empty()){
		int x = q.top().second; q.pop();
		if(vis[x])continue; vis[x] = true;
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to;
			if(dis[v] > dis[x] + e[i].val){
				dis[v] = dis[x] + e[i].val;
				q.push(pli(dis[v], v));
			}
		}
	}
	for(int i = 1; i <= n; ++i)printf("%lld\n",dis[i]);
	return 0;
}

2023省选武汉联测10

A. 矩阵

随机一个 \(1 \times n\) 的向量 \(v\)

判断 $v \times A \times B $ 是否等于 $ v \times C$

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int mod = 998244353, maxn = 3005;
mt19937 rd((ull)&maxn);
int sint(int l, int r){return uniform_int_distribution<>(l, r)(rd);}
int n, a[maxn][maxn], b[maxn][maxn], c[maxn][maxn], v[maxn], v1[maxn], v2[maxn];
bool solve(){
	n = read(); for(int i = 1; i <= n; ++i)v[i] = sint(0, mod - 1), v1[i] = v2[i] = 0;
	for(int i = 1; i <= n; ++i)for(int j = 1; j <= n; ++j)a[i][j] = read();
	for(int i = 1; i <= n; ++i)for(int j = 1; j <= n; ++j)b[i][j] = read();
	for(int i = 1; i <= n; ++i)for(int j = 1; j <= n; ++j)c[i][j] = read();
	for(int k = 1; k <= n; ++k)for(int j = 1; j <= n; ++j)v1[j] = (v1[j] + 1ll * v[k] * a[k][j]) % mod;
	for(int k = 1; k <= n; ++k)for(int j = 1; j <= n; ++j)v2[j] = (v2[j] + 1ll * v[k] * c[k][j]) % mod;
	for(int j = 1; j <= n; ++j)v[j] = v1[j], v1[j] = 0;
	for(int k = 1; k <= n; ++k)for(int j = 1; j <= n; ++j)v1[j] = (v1[j] + 1ll * v[k] * b[k][j]) % mod;
	for(int i = 1; i <= n; ++i)if(v1[i] != v2[i])return false; return true;
}
int main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	int t = read(); for(int i = 1; i <= t; ++i)if(solve())printf("Yes\n"); else printf("No\n");
	return 0;
}

B. 错排

\(f_{n, m}\) 表示 \(n\) 个数, \(m\) 个没有限制的错排

\(m = 0\) 时为普通错排

\[ ans = \frac{(n - m)!}{(n - 2m)!}f_{n - m, m} \]

枚举至少几个位置不满足限制,简单容斥得到

\[ f_{n,m} = \sum_{k = 0}^{n - m}(-1)^{i}\binom{n - m}{i}(n - i)! \]

因为

\[ \binom{n}{m} = \binom{n - 1}{m - 1} + \binom{n - 1}{m} \]

代入上面,变形整理一下,并用 \(m + 1\) 代替 \(m\)

得到

\[ f_{n, m} = f_{n - 1, m - 1} + f_{n, m - 1} \]

也可以由组合意义直接得到,考虑 \(p_m = m\) 则为 \(f_{n - 1, m - 1}\) 否则为 \(f_{n, m - 1}\)

\(f_{n, m}\) 看成多项式

\[ [x^n]F_{m} = f_{n, m} \]

\[ F_{m} = (1 + x) F_{m - 1} \]

\[ F_{m} = (1 + x)^m F_{0} \]

设定块长 \(B\), 预处理 \(F_{kB}\), 每次用组合数和 \(F_{m / B}\)计算 \(f_{n, m}\) 即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int mod = 998244353, maxn = 2e5 + 55, mx = 2e5, mxb = 50, B = 4000, deg = 262144;
int qpow(int x, int y){int ans = 1; for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod; return ans;}
int wn[maxn * 2], rev[maxn * 2];
void init(){
	wn[0] = 1; wn[1] = qpow(3, (mod - 1) / deg);
	for(int i = 1; i < deg; ++i){
		wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
		rev[i] = rev[i >> 1] >> 1;
		if(i & 1)rev[i] |= (deg >> 1);
	}
}
struct poly{
	int f[maxn * 2];
	int &operator [] (const int &i){return f[i];}
	void ntt(){
		for(int i = 1; i < deg; ++i)if(i < rev[i])swap(f[i], f[rev[i]]);
		for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1)
			for(int i = 0; i < deg; i += l)
				for(int j = i, x, y; j < i + hl; ++j){
					x = f[j], y = 1ll * wn[deg / l * (j - i)] * f[j + hl] % mod;
					f[j] = (x + y) % mod; f[j + hl] = (x - y + mod) % mod;
				}
	}
	void intt(){
		ntt(); reverse(f + 1, f + deg); int Inv = qpow(deg, mod - 2);
		for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * Inv % mod;
	}
}f[mxb + 5], fb;
int fac[maxn], ifac[maxn], c[B + 5][B + 5];
int calc(int n, int m){
	int res = 0, b = m / B, r = m % B;
	for(int i = 0; i <= min(r, n); ++i)res = (res + 1ll * c[r][i] * f[b][n - i]) % mod;
	return res;
}
int sol(){
	int n = read(), m = read();
	if(m + m > n)return 0; 
	if(m == 0 || m == 1)return f[0][n];
	return 1ll * calc(n - m, m) * fac[n - m] % mod * ifac[n - m - m] % mod;
}
void prework(){
	fac[0] = ifac[0] = 1; for(int i = 1; i <= mx; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	ifac[mx] = qpow(fac[mx], mod - 2); for(int i = mx - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
	f[0][0] = f[0][2] = 1; for(int i = 3; i <= mx; ++i)f[0][i] = 1ll * (i - 1) * (f[0][i - 1] + f[0][i - 2]) % mod;
	for(int i = 0; i <= B; ++i){
		c[i][0] = 1;
		for(int j = 1; j <= i; ++j)c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
	}
	for(int i = 0; i <= B; ++i)fb[i] = c[B][i];
	init(); fb.ntt(); f[0].ntt();
	for(int i = 1; i <= mxb; ++i)
		for(int j = 0; j < deg; ++j)f[i][j] = 1ll * f[i - 1][j] * fb[j] % mod;
	for(int i = 0; i <= mxb; ++i)f[i].intt();
}
int main(){
	// freopen("qwq.in","r",stdin);
	// freopen("qwq.out","w",stdout);
	prework(); int t = read(); for(int i = 1; i <= t; ++i)printf("%d\n",sol());
	return 0;
}

C. 异或图

先考虑 \(m = 0\) 怎么做,如果一个数在某一位可以填 \(1\) 但是填了 \(0\) 后面可以任意填,那么让其他数任意填,该数有唯一方案使得异或为 \(c\), 那么可以直接在这里计算贡献

那么枚举满足上面条件的最高位,可以在 \(O(nlogc)\) 内得到答案

现在考虑边的限制,钦定一个连通块权值相等,那么他对答案的贡献根据连通块大小的奇偶性可以等效成 \(0 / min(a)\) 直接暴力可以做到 \(2^mnlogw\)

考虑正解,枚举下一个拼上的连通块,容斥系数为 \((-1)^{|边集|}\), 这个边集要求满足使得连通块联通

用类似修建游乐园的方法可以 \(3^n\) 容斥出系数

然后 \(f_{i, j}\) 表示当前连通块集合 \(i\), 有效点(连通块大小为奇数中 \(a\) 最小的点)集合为 \(j\) 的容斥系数之和

发现可以三进制装压,转移枚举下一个拼上的连通块即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

ll read(){
	ll x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int mod = 998244353, maxn = 20;
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int n, m, ans;
ll c, a[maxn];
int p3[maxn], pb[maxn][65], *b[maxn], e[maxn];
int calc(int s){
	ll val = 0;
	for(int i = 0; i < n; ++i)if((s >> i) & 1)val ^= a[i];
	int res = (val == c);
	for(int d = 0; d <= 59; ++d){
		int f0 = 1, f1 = 0, tp = 1; ll o = 0;
		for(int i = 0; i < n; ++i)if((s >> i) & 1){
			int t0 = f0, t1 = f1;
			f0 = (1ll * t0 * b[i][d - 1] + 1ll * t1 * ((a[i] & (1ll << d)) % mod)) % mod;
			f1 = (1ll * t1 * b[i][d - 1] + 1ll * t0 * ((a[i] & (1ll << d)) % mod)) % mod;
			tp = (1ll * tp * b[i][d - 1]) % mod; o ^= (a[i] >> d);
		}
		f0 = (f0 - tp + mod) % mod;
		if((o ^ (c >> d)) == 0)res = (res + 1ll * f0 * qpow((1ll << d) % mod, mod - 2)) % mod;
		if((o ^ (c >> d)) == 1)res = (res + 1ll * f1 * qpow((1ll << d) % mod, mod - 2)) % mod;
	}
	return res;
}
int mis[1 << 15], f[1 << 15], g[1 << 15];
int mp[14348908], h[14348908];
int main(){
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	n = read(), m = read(), c = read();
	for(int i = 0; i < n; ++i)a[i] = read();
	p3[0] = 1; for(int i = 1; i <= n; ++i)p3[i] = 3 * p3[i - 1];
	for(int i = 1; i <= m; ++i){
		int u = read() - 1, v = read() - 1;
		e[u] |= (1 << v); e[v] |= (1 << u);
	}
	for(int i = 0; i < n; ++i){
		b[i] = pb[i] + 1; b[i][-1] = 1;
		for(int j = 0; j <= 59; ++j)b[i][j] = (b[i][j - 1] + (a[i] & (1ll << j))) % mod;
	}
	if(m == 0){printf("%d\n",calc((1 << n) - 1)); return 0;}
	for(int s = 1; s < (1 << n); ++s){
		int val = 0;
		for(int j = 0; j < n; ++j)if((s >> j) & 1)val |= (e[j] & s), mis[s] += p3[j];
		f[s] = g[s] = (!val); int ss = s ^ (s & -s);
		for(int t = ss; ;t = (t - 1) & ss){
			g[s] = (g[s] - 1ll * g[(s & -s) | t] * f[ss ^ t] + mod) % mod;
			if(!t)break;
		}
	}
	a[n] = LLONG_MAX;
	for(int s = 1; s < (1 << n); ++s){
		int id = n; for(int j = 0; j < n; ++j)if(((s >> j) & 1) && a[j] < a[id])id = j;
		if(__builtin_parity(s))mis[s] += p3[id];
		else g[s] = 1ll * g[s] * ((a[id] + 1) % mod) % mod;
	}
	for(int s = 0; s < p3[n]; ++s){
		if(s * 3 < p3[n])mp[s * 3] = mp[s] << 1;
		if(s * 3 + 1 < p3[n])mp[s * 3 + 1] = mp[s] << 1 | 1;
		if(s * 3 + 2 < p3[n])mp[s * 3 + 2] = mp[s] << 1 | 1;
	}
	h[0] = 1;
	for(int s = 0; s < p3[n]; ++s){
		int S = mp[s];
		S = ((1 << n) - 1) ^ S;
		int low = S & -S, SS = S ^ low;
		for(int T = SS; ; T = (T - 1) & SS){
			h[s + mis[T ^ low]] = (h[s + mis[T ^ low]] + 1ll * h[s] * g[T ^ low]) % mod;
			if(!T)break;
		}
	}
	int all = 0;
	for(int i = 0; i < n; ++i)all += p3[i];
	for(int s = 0; s < (1 << n); ++s){
		int t = all;
		for(int i = 0; i < n; ++i)if((s >> i) & 1)t += p3[i];
		ans = (ans + 1ll * calc(s) * h[t]) % mod;
	}
	printf("%d\n",ans);
	return 0;
}

2023省选武汉联测11

A. 游戏

发现 \(u, v, w\) 中间会存在一个点,一个点走向另外两个点的路径在这里分叉

每个点到该中心点的距离好求

找到从每个点出发的向不同子树走的最长的三条链

然后三维偏序找到任意一个满足三个距离的点即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 2e5 + 55;
int n, q, fa[maxn][29], dep[maxn];
vector<int>g[maxn], f[maxn], all;
int LCA(int u, int v){
	if(dep[u] < dep[v])swap(u, v);
	for(int i = 18; i >= 0; --i)if(dep[u] - dep[v] >= (1 << i))u = fa[u][i];
	if(u == v)return u;
	for(int i = 18; i >= 0; --i)if(fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}
int kfa(int x, int k){
	for(int i = 18; i >= 0; --i)if((k >> i) & 1)x = fa[x][i]; 
	return x;
}
int dis(int u, int v){return dep[u] + dep[v] - 2 * dep[LCA(u, v)];}
void dfs(int x){
	for(int i = 1; i <= 18; ++i)fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for(int v : g[x])if(v != fa[x][0]){
		dep[v] = dep[x] + 1; 
		fa[v][0] = x; dfs(v);
	}
}
int now;
bool cmp(int x, int y){return dis(now, x) > dis(now, y);}
void up(int x){
	for(int v : g[x])if(v != fa[x][0]){up(v); f[x].push_back(f[v][0]);}
	now = x; sort(f[x].begin(), f[x].end(), cmp); 
	while(f[x].size() < 3)f[x].push_back(x); f[x].resize(3);
}
void down(int x){
	now = x; sort(f[x].begin(), f[x].end(), cmp); 
	while(f[x].size() < 3)f[x].push_back(x); f[x].resize(3);
	for(int v : g[x])if(v != fa[x][0]){
		if(dis(f[x][0], x) > dis(f[x][0], v))f[v].push_back(f[x][1]);
		else f[v].push_back(f[x][0]);
		down(v);
	}
}
struct data{
	int pos, val;
	friend data operator + (const data &x, const data &y){
		return x.val == y.val ? (x.pos > y.pos ? x : y) : (x.val > y.val ? x : y);
	}
};
struct seg{
	data t[maxn << 2 | 1];
	void modify(int x, int l, int r, int pos, data val){
		if(l == r){t[x] = t[x] + val; return;}
		int mid = (l + r) >> 1;
		if(pos <= mid)modify(x << 1, l, mid, pos, val);
		else modify(x << 1 | 1, mid + 1, r, pos, val);
		t[x] = t[x << 1] + t[x << 1 | 1];
	}
	data query(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x];
		int mid = (l + r) >> 1;
		if(R <= mid)return query(x << 1, l, mid, L, R);
		if(L > mid)return query(x << 1 | 1, mid + 1, r, L, R);
		return query(x << 1, l, mid, L, R) + query(x << 1 | 1, mid + 1, r, L, R);
	}
}T;
int rid[maxn], tmp[maxn];
struct node{
	int u, v, w, d[3], pos, id;
	void init(){
		int uv = read(), uw = read(), vw = read();
		u = (uv + uw - vw) / 2, v = (vw + uv - uw) / 2, w = (uw + vw - uv) / 2;
		d[0] = u; d[1] = v; d[2] = w; sort(d, d + 3); reverse(d, d + 3);
	}
	friend bool operator < (const node &x, const node &y){return x.d[0] > y.d[0];}
}d[maxn];
bool cmp1(int x, int y){return tmp[x] < tmp[y];}
int rem[3];
int find(int x, int v, int dis){
	if(dis == 0)return x;
	int lca = LCA(x, v);
	if(lca == x)return kfa(v, dep[v] - dep[x] - dis);
	if(lca == v)return kfa(x, dis);
	if(dep[x] - dep[lca] >= dis)return kfa(x, dis);
	dis = dis - dep[x] + dep[lca];
	return kfa(v, dep[v] - dep[lca] - dis);
}
void print(node &x){
	for(int i = 0; i < 3; ++i)rem[i] = find(x.pos, f[x.pos][i], x.d[i]);
	for(int i = 0; i < 3; ++i)if(x.d[i] == x.u){printf("%d ",rem[i]); x.d[i] = -1; break;}
	for(int i = 0; i < 3; ++i)if(x.d[i] == x.v){printf("%d ",rem[i]); x.d[i] = -1; break;}
	for(int i = 0; i < 3; ++i)if(x.d[i] == x.w){printf("%d",rem[i]); x.d[i] = -1; break;}
	printf("\n");
}
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	n = read(); 
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	dfs(1); up(1); down(1);
	q = read(); for(int i = 1; i <= q; ++i)d[i].init(), d[i].id = i;
	for(int i = 1; i <= n; ++i)tmp[i] = dis(i, f[i][0]);
	sort(d + 1, d + q + 1); for(int i = 1; i <= q; ++i)rid[d[i].id] = i;
	for(int i = 1; i <= n; ++i)all.push_back(i); sort(all.begin(), all.end(), cmp1);	
	int p = 1;
	for(int i = n; i >= 0; --i){
		while(!all.empty() && dis(all.back(), f[all.back()][0]) == i){
			T.modify(1, 0, n, dis(all.back(), f[all.back()][1]), data{all.back(), dis(all.back(), f[all.back()][2])});
			all.pop_back();
		}
		while(p <= q && d[p].d[0] == i){
			d[p].pos = T.query(1, 0, n, d[p].d[1], n).pos;
			++p;
		}
	}
	for(int i = 1; i <= q; ++i)print(d[rid[i]]);
	return 0;
}

B. 马戏团里你最忙

首先用 \(fwt\) 暴力 \(f_{i, s}\) 表示第 \(i\) 轮,得到的数为 \(s\) 的期望

\(f_i = \sum_{j = 1}^{m}f_{i - j}a_{j - 1}\)

满足线性递推

先暴力出前几项,然后高斯消元得到系数。

阿巴阿巴阿巴阿巴

code

C. 树

考虑用类似倍增的东西处理

如果是一条链

\(f_{i, j}\) 表示从 \(i\) 开始 \(2^j\) 个数的对 \(i\) 的答案

\[ f_{i, j} = f_{i, j - 1} + f_{i + 2^{j - 1}, j - 1} + \sum_{k = i + 2^{j - 1}}^{i + 2^j - 1} 2^{j - 1}- 2^j \times \sum_k[v_k >> (j - 1) \& 1] \]

在树上可以维护每个点不停走右儿子左侧的答案

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 1e6 + 55;
int n, m, v[maxn];
int fa[maxn], son[maxn][21], las[maxn], dep[maxn], pre[maxn];
ll f[maxn][21], si[maxn], sum[maxn][21];
vector<int>g[maxn], dx[maxn];
void dfs(int x){
	son[x][0] = las[dep[x] + 1];
	pre[x] = las[dep[x]];
	las[dep[x]] = x;
	dx[dep[x]].push_back(x);
	for(int v : g[x])dep[v] = dep[x] + 1, dfs(v), son[x][0] = v;
}
void rdfs(int x){
	for(int v : g[x])rdfs(v);
	si[x] += si[son[x][0]];
	for(int i = 0; i <= 20; ++i)sum[x][i] += sum[son[x][0]][i];
}
ll calc(int x, int k){
	if(x == 0)return 0;
	++k;
	ll res = 0; int t = x;
	for(int i = 20; i >= 0; --i)if(k >> i & 1)res += f[x][i], x = son[x][i];
	int v = x; x = t;
	for(int i = 20; i >= 0; --i)if(k >> i & 1)x = son[x][i], res += (si[x] - si[v]) * (1 << i) - (sum[x][i] - sum[v][i]) * (1 << (i + 1));
	return res;
}
int main(){
	// freopen("tree.in","r",stdin);
	// freopen("tree.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)v[i] = read();
	for(int i = 2; i <= n; ++i)g[fa[i] = read()].push_back(i);
	dep[1] = 1; dfs(1);
	for(int d = 1; d <= n; ++d)for(int p = 0; p < dx[d].size(); ++p){
		int x = dx[d][p]; 
		f[x][0] = v[x]; si[x] = 1;
		for(int i = 0; i <= 20; ++i)sum[x][i] = (v[x] >> i & 1);
		if(p == 0)continue;
		int pr = dx[d][p - 1];
		si[x] += si[pr]; f[x][0] += f[pr][0];
		for(int i = 0; i <= 20; ++i)sum[x][i] += sum[pr][i];
	}
	rdfs(1);
	for(int i = 1; i <= 20; ++i)
		for(int x = 1; x <= n; ++x){
			son[x][i] = son[son[x][i - 1]][i - 1];
			f[x][i] = f[x][i - 1] + f[son[x][i - 1]][i - 1] + (si[son[x][i - 1]] - si[son[x][i]]) * (1 << (i - 1)) - (sum[son[x][i - 1]][i - 1] - sum[son[x][i]][i - 1]) * (1 << i);
		}
	m = read();
	for(int i = 1; i <= m; ++i){
		int x = read(), k = read();
		printf("%lld\n",calc(x, k) - calc(pre[x], k));
	}
	return 0;
}


2023省选武汉联测12

A. 图案

直接 \(kmp\)\(nxt\) 得到每个周期判断就能过

复杂度显然不太对,但是没啥特别好的卡法

而且好像有个什么结论可以把 \(border\) 划分成 \(log\) 个等差数列,优化一下说不定复杂度是对的??

不过大部分的做法是直接枚举长度 \(hash\) 然后找 \(lcp\) 贡献

垃圾做法
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 1e6 + 55;
int n, k, nxt[maxn]; 
char s[maxn];
int main(){
	freopen("pattern.in","r",stdin);
	freopen("pattern.out","w",stdout);
	scanf("%d%d",&n,&k); scanf("%s", s + 1);
	for(int i = 2, j = 0; i <= n; ++i){
		while(j && s[j + 1] != s[i])j = nxt[j];
		if(s[j + 1] == s[i])++j;
		nxt[i] = j;
	}
	for(int i = 1; i <= n; ++i){
		int now = nxt[i];
		while(true){
			int len = i - now;
			if((i / len) % k == 0 || (i % len == 0 && (i / len) % (k + 1) == 0)){
				printf("1"); break;
			}
			if((i / len) < k || len == i){
				printf("0"); break;
			}
			now = nxt[now];
		}		
	}
	printf("\n");
	return 0;
}

B. 树点购买

我是sb

发现一棵子树内最多一个点不知道 \(f_{x, 1 / 0}\) 表示子树内有没有一个不知道的最小代价

设一个 \(g_{x, 1 / 0}\) 作为方案数

不过我考场是另一个思路,解决前两问比较方便

考虑一个子树内有某些点的权值可以通过外面的点作差得到,称这样的点是可替换的,先把所有贡献统计下来,再记录可替换的最大权值,考虑是否替换即可

第二问在每个点维护两个 \(vector\) 分别表示子树内已经确定的点和可以替换的点,启发式合并即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 1e6 + 55, mod = 998244353;
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int n, c[maxn], fa[maxn];
vector<int>g[maxn], rem[maxn], tmp[maxn];
ll f[maxn]; int mx[maxn];
void merge(vector<int>&a, vector<int>&b){
	if(b.size() > a.size())swap(a, b);
	for(int v : b)a.push_back(v); b.clear();
}
void dfs(int x){
	bool leaf = true; int mxp = 0; 
	// cf[x] = ctmp[x] = 1;
	for(int v : g[x])if(v != fa[x]){
		fa[v] = x; dfs(v);
		f[x] += f[v]; leaf = false;
		if(mx[v] > mx[x]){mxp = v, mx[x] = mx[v]; }
		else if(mx[v] == mx[x]){mxp = -1;}
		merge(rem[x], rem[v]); 
	}
	if(leaf){f[x] = mx[x] = c[x]; tmp[x].push_back(x);}
	else{
		if(mx[x] > c[x]){
			f[x] = f[x] + c[x] - mx[x];
			mx[x] = c[x];
			tmp[x].push_back(x); 
			for(int v : g[x])if(v != fa[x] && v != mxp){merge(rem[x], tmp[v]); }
		}else if(mx[x] == c[x]){
			tmp[x].push_back(x);
			for(int v : g[x])if(v != fa[x]){
				if(v == mxp){merge(tmp[x], tmp[v]);}
				else {merge(rem[x], tmp[v]); }
			}
		}else{
			for(int v : g[x])if(v != fa[x]){
				if(v == mxp){merge(tmp[x], tmp[v]);}
				else {merge(rem[x], tmp[v]);}
			}
		}
	}
}
ll val[maxn][2];
int cf[maxn][2];
bool can[maxn];
int pre[maxn], suf[maxn];
void solve(int x){
	bool leaf = true; cf[x][0] = 1;
	int las = 1;
	for(int v : g[x])if(v != fa[x]){
		leaf = false;
		solve(v);
		val[x][0] += val[v][0]; 
		val[x][1] = min(val[x][1], val[v][1] - val[v][0]);
		cf[x][0] = 1ll * cf[x][0] * cf[v][0] % mod;
		pre[v] = las; las = 1ll * las * cf[v][0] % mod;
	}
	if(leaf){
		cf[x][0] = can[x]; 
		val[x][0] = c[x];
		cf[x][1] = 1; 
		return;
	}
	las = 1;
	for(int i = g[x].size() - 1; i >= 0; --i)if(g[x][i] != fa[x]){
		int v = g[x][i]; suf[v] = las; las = 1ll * las * cf[v][0] % mod;
	}
	for(int v : g[x])if(v != fa[x]){
		if(val[x][1] == val[v][1] - val[v][0])cf[x][1] = (cf[x][1] + 1ll * pre[v] * suf[v] % mod * cf[v][1]) % mod;
	}
	val[x][1] = val[x][0] + val[x][1];
	if(val[x][0] > val[x][1] + c[x])val[x][0] = val[x][1] + c[x], cf[x][0] = cf[x][1]; 
	else if(val[x][1] + c[x] == val[x][0])cf[x][0] = (cf[x][0] + cf[x][1]) % mod;
}
int main(){
	freopen("purtree.in","r",stdin);
	freopen("purtree.out","w",stdout);
	n = read();for(int i = 1; i <= n; ++i)c[i] = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	dfs(1); 
	int k = read();
	if(k >= 1)printf("%lld\n",f[1]);
	if(k >= 2){
		merge(rem[1], tmp[1]);
		sort(rem[1].begin(), rem[1].end());
		for(int v : rem[1])printf("%d ",v); printf("\n");
	}
	if(k >= 3){
		for(int v : rem[1])can[v] = true;
		solve(1); 
		// printf("%lld\n",val[1][0]);
		printf("%d\n",cf[1][0]);
	}
	return 0;
}

C. 舰队游戏

https://www.luogu.com.cn/problem/AT_arc016_4

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 105;
const double eps = 1e-8, inf = 1e12;
int n, m, h, d[maxn];
vector<int>g[maxn];
double base, f[maxn][maxn];
bool vis[maxn];
void dfs(int x){
	vis[x] = true; if(x == n)return;
	for(int i = 1; i <= h; ++i)f[x][i] = 0;
	int deg = g[x].size();
	for(int v : g[x]){
		if(!vis[v])dfs(v);
		for(int i = 1; i <= h; ++i){
			if(i > d[v])f[x][i] += f[v][i - d[v]];
			else f[x][i] = inf;
		}
	}
	for(int i = 1; i <= h; ++i)
		if(!deg)f[x][i] = base + h - i;
		else if(x == 1 && i == h)f[x][i] = f[x][i] / deg + 1;
		else f[x][i] = min(f[x][i] / deg + 1, base + h - i);

}
double solve(double mid){
	base = mid; 
	for(int i = 1; i <= n; ++i)vis[i] = false;
	dfs(1);
	if(!vis[n])return inf;
	return f[1][h];
}
int main(){
	freopen("kancolle.in","r",stdin);
	freopen("kancolle.out","w",stdout);
	n = read(), m = read(), h = read();
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read();
		g[u].push_back(v);
	}
	for(int i = 1; i <= n; ++i)d[i] = read();
	double l = 0, r = 1e6 + 5;
	while(r - l > eps){
		double mid = (l + r) / 2;
		if(solve(mid) > mid) l = mid;
		else r = mid;
	}
	if(l > 1e6)printf("-1");
	else printf("%.10lf\n",l);
	return 0;
}

2023省选武汉联测13

两个构造一个交互,你管这个叫省选模拟?

A. 构树

没啥可说的

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 1005;
int n, l[maxn], r[maxn];
int mp[maxn][maxn];
int f[maxn], cnt;
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
bool merge(int x, int y){x = fa(x); y = fa(y); if(x == y)return false; f[y] = x; return true;}
bool add(int x, int y){
	if(mp[x][y])return false;
	if(merge(x, y) == false)return true;
	mp[x][y] = mp[y][x] = true; ++cnt;
	return false;
}
bool solve(){
	for(int i = 1; i <= n; ++i){
		if(i < l[l[i]] || i > r[l[i]])return false;
		if(i < l[r[i]] || i > r[r[i]])return false;
		if(add(i, l[i]))return false; 
		if(add(i, r[i]))return false;
	}
	for(int i = 1; i <= n; ++i)
		for(int j = l[i] + 1; j < r[i]; ++j)if(l[j] <= i && r[j] >= i && fa(i) != fa(j))add(i, j);
	return cnt == n - 1;
}
int main(){
   freopen("tree.in","r",stdin);
   freopen("tree.out","w",stdout); 
	n = read();
	for(int i = 1; i <= n; ++i)f[i] = i;
	for(int i = 1; i <= n; ++i)l[i] = read(), r[i] = read();
	if(solve()){
		for(int i = 1; i <= n; ++i)
			for(int j = i + 1; j <= n; ++j)
				if(mp[i][j])printf("%d %d\n",i, j);
	}
	else printf("-1\n");
	return 0;
}

B. 交互

依次考虑每个点,维护一个类似右链的东西

查询当前点是不是叶子,如果不是先不管

是的话,查询上个栈里的点是不是他的父亲,如果不是,下一个没有加入的数就是他的父亲

处理一下边界情况即可

code
#include<bits/stdc++.h>
#include<cstdlib>
#include"interact.h"

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 105;
int st[maxn], top, l[maxn], r[maxn];

void guess(int n){
	for(int i = 1; i <= n; ++i)l[i] = r[i] = i;
	for(int i = 1; i <= n; ++i){
		if(query(i, l[i], i)){
			if(top){
				st[++top] = i;
				while(top > 1 && query(st[top - 1], l[st[top - 1]], r[st[top]])){
					report(st[top - 1], st[top]);
					r[st[top - 1]] = r[st[top]];
					--top;
				}
				if(top && i != n){
					report(i + 1, st[top]);
					l[i + 1] = l[st[top]];
					--top;
				}
			}else{
				if(i == n || query(i, 1, n)){
					l[i] = 1; r[i] = n; st[++top] = i;
				}else{
					report(i + 1, i);
					l[i + 1] = l[i];
				}
			}
		}else st[++top] = i;
	}
	for(int i = top; i > 1; --i)report(st[i], st[i - 1]);
}

C. 构图

首先不难发现只用度数为 \(k\)\(k + 1\) 的点就能构造出最优方案

度数为 \(k\) 的点只能向 \(k + 1\) 的点连边,总度数又要求最小,所以找到最小的数量 \(x\), 使得 \((k + 1)x >= k(n - x)\) 此时达到最优情况

然后连边的话什么奇怪做法都有,我的连边好像有问题,但是过了。

code
// ubsan: undefined
// accoders
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
int n, k;
vector<pii>edge;
void print(){
	printf("%d\n",(int)edge.size());
	for(pii v : edge)printf("%d %d\n",v.first, v.second);
}
void sol1(){
	for(int i = 1; i <= n; i += k + k + 1){
		for(int j = 0; j < k; ++j)
			for(int p = k; p < k + k + 1; ++p)
				edge.push_back(pii(i + j, i + p));
	}
}
void sol2(){
	if(n % (k + k + 1) == 1){
		n -= 6;
		for(int i = 1; i <= 4; ++i)
			for(int j = 5; j <= 6; ++j)
				edge.push_back(pii(n + i, n + j));
	}
	if(n % (k + k + 1) == 2){
		n -= 7;
		edge.push_back(pii(n + 1, n + 4));
		edge.push_back(pii(n + 1, n + 5));
		edge.push_back(pii(n + 2, n + 5));
		edge.push_back(pii(n + 2, n + 6));
		edge.push_back(pii(n + 3, n + 6));
		edge.push_back(pii(n + 3, n + 7));
		edge.push_back(pii(n + 4, n + 5));
		edge.push_back(pii(n + 6, n + 7));
		edge.push_back(pii(n + 4, n + 7));
	}
	if(n % (k + k + 1) == 3){
		n -= 4;
		for(int i = 1; i <= 2; ++i)
			for(int j = 3; j <= 4; ++j)
				edge.push_back(pii(n + i, n + j));
		edge.push_back(pii(n + 3, n + 4));
		n -= 4;
		for(int i = 1; i <= 2; ++i)
			for(int j = 3; j <= 4; ++j)
				edge.push_back(pii(n + i, n + j));
		edge.push_back(pii(n + 3, n + 4));
	}
	if(n % (k + k + 1) == 4){
		n -= 4;
		for(int i = 1; i <= 2; ++i)
			for(int j = 3; j <= 4; ++j)
				edge.push_back(pii(n + i, n + j));
		edge.push_back(pii(n + 3, n + 4));
	}
	sol1();
}
const int maxn = 1e5 + 55;
vector<int>vec;
void solve(){
	int k1 = (k & 1) && (n & 1);
	while(k1 * (k + 1) < (n - k1) * k)k1 += 2;
	for(int j = 1; j <= k + 1; ++j)
		for(int i = 1; i <= k1; ++i)
			vec.push_back(i);
	for(int i = k1 + 1; i <= n; ++i)
		for(int j = 1; j <= k; ++j){
			edge.push_back(pii(vec.back(), i));
			vec.pop_back();
		}
	while(vec.size()){
		int u = vec.back(); vec.pop_back();
		int v = vec.back(); vec.pop_back();
		edge.push_back(pii(u, v));
	}
}
int main(){
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	n = read(), k = read();
	if(n % (k + k + 1) == 0)sol1();
	else{
		if(k == 2)sol2();
		else{
			solve();
		}
	}
	print();
	return 0;
}

posted @ 2023-03-23 20:16  Chen_jr  阅读(20)  评论(0编辑  收藏  举报