AtCoder Beginner Contest 213

相隔大约一周才补题,原因是太弱了。

H 正解是分治 FFT,目前不会所以留坑在这里。

ABC213

\(\mathcal A\sim E\)

赛时平均一题 5 min,而且主要是读题时间,所以放在一起提一句。

A 异或基本运算,B 排序,C 离散化,D DFS 序,E 0/1 BFS,每以一个障碍物为中心的 \(3\times 3\) 均为 \(1\) 步到达。

代码懒得放。

\(\mathcal F\)

针对一个字符串的每个后缀,求出它与所有后缀的 LCP 之和。

比较套路的 SA + 单调栈。

考虑 SA,每个点对当前位的贡献通过单调栈简单维护即可,正反各扫一遍。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 1e6 + 10;
int n, tot, stk[N], wid[N];
int bac[N], sa[N], rk[N], SA[N], RK[N], lcp[N];
char str[N]; LL ans[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

void Get_SA(){
	for(int i = 1; i <= n; i ++) bac[str[i]] ++;
	for(int i = 1; i <= 256; i ++) bac[i] += bac[i - 1];
	for(int i = 1; i <= n; i ++) sa[bac[str[i]] --] = i;
	for(int i = 1; i <= n; i ++) rk[sa[i]] = rk[sa[i - 1]] + (str[sa[i]] != str[sa[i - 1]]);

	for(int p = 1; p <= n; p <<= 1){
		for(int i = 1; i <= n; i ++) bac[rk[sa[i]]] = i;
		for(int i = n; i >= 1; i --) if(sa[i] > p) SA[bac[rk[sa[i] - p]] --] = sa[i] - p;
		for(int i = n; i > n - p; i --) SA[bac[rk[i]] --] = i;
		#define comp(x, y) (rk[x] != rk[y] || rk[x + p] != rk[y + p])
		for(int i = 1; i <= n; i ++) RK[SA[i]] = RK[SA[i - 1]] + comp(SA[i - 1], SA[i]);
		for(int i = 1; i <= n; i ++) sa[i] = SA[i], rk[i] = RK[i];
		if(rk[sa[n]] == n) break;
	}
	
	for(int i = 1; i <= n; i ++){
		int j = sa[rk[i] - 1], k = max(lcp[rk[i - 1]] - 1, 0);
		while(str[i + k] == str[j + k] && str[i + k]) k ++;
		lcp[rk[i]] = k;
	}
}

int main(){
	scanf("%d", &n);
	scanf("%s", str + 1);
	Get_SA();
	LL now;
	tot = now = 0;
	for(int i = 2; i <= n; i ++){
		int width = 1;
		while(tot && stk[tot] >= lcp[i]){
			now -= 1LL * wid[tot] * stk[tot];
			width += wid[tot];
			tot --;
		}
		stk[++ tot] = lcp[i]; wid[tot] = width;
		now += 1LL * wid[tot] * stk[tot];
		ans[sa[i]] += now;
	}
	tot = now = 0;
	for(int i = n; i >= 1; i --){
		ans[sa[i]] += now;
		int width = 1;
		while(tot && stk[tot] >= lcp[i]){
			now -= 1LL * wid[tot] * stk[tot];
			width += wid[tot];
			tot --;
		}
		stk[++ tot] = lcp[i]; wid[tot] = width;
		now += 1LL * wid[tot] * stk[tot];
	}
	for(int i = 1; i <= n; i ++) printf("%lld\n", ans[i] + n - i + 1);
	return 0;
}

\(\mathcal G\)

给定一个无向图,任意选择一些边,对于每个点 \(2\leq k\leq n\),求出使 \(1,k\) 联通的方案数。

这个大写 G 的标题怎么这么鬼畜 🤒

好像见过类似的状压 DP 题的样子...,不知道为什么赛场上没想出来 😢

\(f(S)\) 表示点集 \(S\) 联通的方案数,\(g(S)\) 表示点集 \(S\) 任意,\(V\) 表示点集全集,且没有出边的方案数。那么:

\[Ans(k)=\sum\limits_{1,k\in S\subset V} f(S)\times g(V-S) \]

只要预处理出 \(f,g\),就可以 \(O(n2^n)\) 统计答案。

\(g\) 是比较好求的,设两端点都在点集 \(S\) 的边的数量为 \(e\),那么 \(g(S)=2^e\)。复杂度 \(O(m2^n)\)

最后利用容斥求出 \(f(S)\),考虑某个点 \(u\),将它作为代表元进行容斥:

\[f(S)=g(S)-\sum\limits_{u\in T\subseteq S} f(T)g(S-T) \]

利用枚举子集的方法可以做到 \(O(3^n)\),这也是复杂度瓶颈。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 17;
const int M = 1 << N;
const LL MOD = 998244353;

int n, m, x[N * N], y[N * N];
LL f[M], g[M], ans[N], Pow[N * N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

int main(){
	n = read(), m = read();
	for(int i = 1; i <= m; i ++)
		x[i] = read() - 1, y[i] = read() - 1;
	Pow[0] = 1;
	for(int i = 1; i <= m; i ++) Pow[i] = Pow[i - 1] * 2 % MOD;

	for(int s = 0; s < (1 << n); s ++){
		int c = 0;
		for(int i = 1; i <= m; i ++)
			if((s >> x[i] & 1) && (s >> y[i] & 1)) c ++;
		g[s] = Pow[c];
	}

	for(int s = 0; s < (1 << n); s ++){
		f[s] = g[s];
		int u;
		for(int i = 0; i < n; i ++)
			if(s >> i & 1) {u = i; break;}
		for(int t = s & (s - 1); t; t = (t - 1) & s)
			if(t >> u & 1) f[s] = (f[s] - f[t] * g[s ^ t] % MOD + MOD) % MOD;
	}
	
	for(int s = 0; s < (1 << n); s ++){
		if(!(s & 1)) continue;
		for(int i = 1; i < n; i ++)
			if(s >> i & 1) ans[i] = (ans[i] + f[s] * g[((1 << n) - 1) ^ s] % MOD) % MOD;
	}
	
	for(int i = 1; i < n; i ++) printf("%lld\n", ans[i]);
	return 0;
}
posted @ 2021-08-12 18:42  LPF'sBlog  阅读(82)  评论(0编辑  收藏  举报