2023冲刺国赛模拟20

2023冲刺国赛模拟20

越来越废物了。

A. 树染色

\(f_{x, 1 / 0}\) 表示考虑 \(x\) 子树内,第一条链为黑色/白色,不考虑第一条链在子树外方案数的答案。

转移枚举第一条链是哪个,用组合数给各个子树的链定序。

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 = 5e5 + 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, head[maxn], tot;
struct edge{int to, net, a, b;}e[maxn << 1 | 1];
void add(int u, int v, int a, int b){
	e[++tot] = {v, head[u], a, b};
	head[u] = tot;
}
int fac[maxn], ifac[maxn];
int c(int n, int m){
	return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
int f[maxn][2], dep[maxn], cnt[maxn];
void solve(int x, int fa){
	int sp = 1, base = 1; 
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to; if(v == fa)continue;
		dep[v] = dep[x] + 1; 
		solve(v, x); 
		cnt[x] += cnt[v];
		base = 1ll * base * ifac[cnt[v]] % mod;
		f[v][0] = 1ll * f[v][0] * e[i].a % mod;
		f[v][1] = 1ll * f[v][1] * e[i].b % mod;
		sp = 1ll * sp * (f[v][0] + f[v][1]) % mod * dep[x] % mod;
	}
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to; if(v == fa)continue;
		int tmp = 1ll * sp * qpow(1ll * (f[v][0] + f[v][1]) % mod * dep[x] % mod, mod - 2) % mod;
		f[x][0] = (f[x][0] + 1ll * f[v][0] * tmp % mod * base % mod * fac[cnt[v]] % mod * fac[cnt[x] - cnt[v]] % mod * c(cnt[x] - 1, cnt[v] - 1)) % mod;
		f[x][1] = (f[x][1] + 1ll * f[v][1] * tmp % mod * base % mod * fac[cnt[v]] % mod * fac[cnt[x] - cnt[v]] % mod * c(cnt[x] - 1, cnt[v] - 1)) % mod;
	}
	if(!cnt[x])f[x][0] = f[x][1] = cnt[x] = 1;
}
int main(){
	freopen("treecolor.in","r",stdin);
	freopen("treecolor.out","w",stdout);
	n = read(); 
	for(int i = 1; i < n; ++i){
		int x = read(), y = read(), a = read(), b = read(); 
		add(x, y, a, b); add(y, x, a, b);
	}
	fac[0] = ifac[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	ifac[n] = qpow(fac[n], mod - 2); for(int i = n - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
	dep[1] = 1; solve(1, 0); 
	printf("%d\n",(f[1][0] + f[1][1]) % mod);
	return 0;
}

B. 关路灯

预处理转向点,发现对于每个点出发,只有 \(log\) 个转向点。

对其扫描线。

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;
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;
int n, m, a[maxn], st[20][maxn];
ll ans[maxn];
vector<pii>ql[maxn], qr[maxn];
vector<pil>pl[maxn], pr[maxn];
int queryl(int x, int lim){
	for(int i = 18; i >= 0; --i)if(x - (1 << i) + 1 >= 1 && st[i][x - (1 << i) + 1] < lim)x -= (1 << i);
	return x;
}
int queryr(int x, int lim){
	for(int i = 18; i >= 0; --i)if(x + (1 << i) - 1 <= n && st[i][x] <= lim)x += (1 << i);
	return x;
}
pli operator + (const pli &x, const pli &y){return pli(x.first + y.first, x.second + y.second);}
pli operator - (const pli &x, const pli &y){return pli(x.first - y.first, x.second - y.second);}
struct BIT{
	pli t[maxn];
	int lowbit(int x){return x & -x;}
	void add(int x, pli val){while(x <= n)t[x] = t[x] + val, x += lowbit(x);}
	pli query(int x){
		pli ans;
		while(x){ans = ans + t[x]; x -= lowbit(x);}
		return ans;
	}
	pli query(int l, int r){return query(r) - query(l - 1);}
	void clear(){memset(t, 0, sizeof(t));}
}T;
int main(){
	freopen("light.in","r",stdin);
	freopen("light.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= m; ++i){
		int l = read(), r = read();
		ans[i] = 1ll * (r - l + 1) * (a[r] - a[l]);
		if(r != l){
			ql[l].push_back(pii(r, i));
			qr[r].push_back(pii(l, i));
		}
	}
	st[0][1] = INT_MAX;
	for(int i = 2; i <= n; ++i)st[0][i] = a[i] - a[i - 1];
	for(int i = 1; (1 << i) <= n; ++i)
		for(int j = 1; j + (1 << i) - 1 <= n; ++j)
			st[i][j] = max(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
	for(int i = 1; i <= n; ++i){
		int l = i, r = i, p = i;
		ll sum = 0;
		while(l != 1 && r != n){
			int pre = a[p] - a[l - 1], nxt = a[r + 1] - a[p];
			if(pre < nxt){
				int np = queryl(l, nxt);
				pl[l - 1].push_back(pil(r, sum + a[p]));
				pl[np - 1].push_back(pil(-r, sum + a[p]));
				sum += a[p] - a[np];
				l = p = np;
			}else{
				int np = queryr(r + 1, pre) - 1;
				pr[r + 1].push_back(pil(l, sum - a[p]));
				pr[np + 1].push_back(pil(-l, sum - a[p]));
				sum += a[np] - a[p];
				r = p = np;
			}
		}
	}
	for(int i = 1; i <= n; ++i){
		for(auto it : pr[i]){
			if(it.first > 0)T.add(it.first, pli(it.second, 1));
			else T.add(-it.first, pli(-it.second, -1));
		}
		for(auto it : qr[i]){
			pli res = T.query(it.first + 1, n);
			ans[it.second] += res.first + 1ll * res.second * a[i];
		}
	}
	T.clear();
	for(int i = n; i >= 1; --i){
		for(auto it : pl[i])
			if(it.first > 0)T.add(it.first, pli(it.second, 1));
			else T.add(-it.first, pli(-it.second, -1));
		for(auto it : ql[i]){
			pli res = T.query(1, it.first - 1);
			ans[it.second] += res.first - 1ll * res.second * a[i];
		}
	}
	for(int i = 1; i <= m; ++i)printf("%lld\n",ans[i]);
	return 0;
}

C. 树状数组

\(f_{i, j, k}\) 表示考虑了前 \(i\) 个位置,当前与 \(r\) 的不同的二进制位最高为 \(j\)\(j\) 及以下有 \(k\)\(1\) 的方案数。

倒着推一个转移系数 \(g_{i, j, k}\)

对于询问把前后缀拼起来。

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 = 1e5 + 55, mod = 998244353;
int n, k, lim, id[25][25], tot, tr[25 * 25][2], f[maxn][25 * 25], g[maxn][25 * 25];
char s[maxn];
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
int lowbit(int x){return x & -x;}
int tr0(int x){return x - lowbit(x);}
int tr1(int x){return x + lowbit(((1 << k) - 1) ^ x);}
int find(int x){
	for(int i = k - 1; i >= 0; --i)if(((x ^ lim) >> i) & 1)return id[i][__builtin_popcount(x & ((1 << i) - 1))];
	return 0;
}
int main(){
	freopen("fenwick.in","r",stdin);
	freopen("fenwick.out","w",stdout);
	n = read(), k = read(); lim = read(); scanf("%s",s + 1);
	for(int i = 0; i < k; ++i)
		for(int j = 0; j <= i; ++j)id[i][j] = ++tot;
	for(int i = 0; i < k; ++i)
		for(int j = 0; j <= i; ++j){
			tr[id[i][j]][0] = j ? id[i][j - 1] : find(tr0(((lim >> i) ^ 1) << i));
			tr[id[i][j]][1] = j == i ? find(tr1((((lim >> i) ^ 1) << i) | ((1 << i) - 1))) : id[i][j + 1];
		}
	tr[find(lim)][0] = find(tr0(lim));
	tr[find(lim)][1] = find(tr1(lim));
	f[0][find(0)] = 1;
	for(int i = 1; i <= n; ++i)
		for(int j = 0; j <= tot; ++j){
			if(s[i] != '1')add(f[i][tr[j][0]], f[i - 1][j]);
			if(s[i] != '0')add(f[i][tr[j][1]], f[i - 1][j]);
		}
	for(int i = 0; i <= lim; ++i)g[n + 1][find(i)] = 1;
	for(int i = n; i >= 1; --i)
		for(int j = 0; j <= tot; ++j){
			if(s[i] != '1')add(g[i][j], g[i + 1][tr[j][0]]);
			if(s[i] != '0')add(g[i][j], g[i + 1][tr[j][1]]);
		}
	for(int i = 1; i <= n; ++i)
		if(s[i] == '1')printf("0\n");
		else{
			int ans = 0;
			for(int s = 0; s <= tot; ++s)add(ans, 1ll * f[i - 1][s] * g[i + 1][tr[s][0]] % mod);
			printf("%d\n",ans);
		}
	return 0;
}
posted @ 2023-06-18 10:13  Chen_jr  阅读(34)  评论(0编辑  收藏  举报