The 2021 ICPC Asia Shenyang Regional Contest
L. Perfect Matchings
标签:容斥原理,树形 $\mathrm{DP}$
如果没有删边的限制就是组合数直接算,但是有一些边是不能选的.
不妨考虑利用容斥原理来做,钦定选上 $\mathrm{i}$ 条树边,然后扣掉点后剩下的边随便选.
然后这个就符合二项式反演中的公式,直接反演就能求出 $\mathrm{g[i]}$.
这里恰好 $\mathrm{i=0}$, 所以直接上容斥的公式即可,时间复杂度是树形 $\mathrm{DP}$ 的 $\mathrm{O(n^2)}$.
#include <bits/stdc++.h>
#define N 4009
#define ll long long
#define pb push_back
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
const int mod = 998244353;
vector<int>G[N];
int n, size[N], dp[N][N][2], tmp[N][2];
// dp[x][j][0/1]: 当前为 x, 一共选了 j 对,x 是否被匹配了.
int ADD(int x, int y) {
return (ll)(x + y) % mod;
}
int DEC(int x, int y) {
return (ll)(x - y + mod) % mod;
}
void dfs(int x, int ff) {
size[x] = 1;
dp[x][0][0] = 1;
for(int i = 0; i < G[x].size() ; ++ i) {
int v = G[x][i];
if(v == ff) continue;
dfs(v, x);
for(int a = 0; a <= size[x] + size[v]; ++ a)
tmp[a][0] = tmp[a][1] = 0;
for(int a = 0; a <= size[x] / 2; ++ a) {
for(int b = 0; b <= size[v] / 2; ++ b) {
// 前面贡献了 a, v 里面贡献了 b
tmp[a + b][0] = ADD(tmp[a + b][0], (ll)dp[x][a][0] * ADD(dp[v][b][0], dp[v][b][1]) % mod);
tmp[a + b][1] = ADD(tmp[a + b][1], (ll)dp[x][a][1] * ADD(dp[v][b][0], dp[v][b][1]) % mod);
tmp[a + b + 1][1] = ADD(tmp[a + b + 1][1], (ll)dp[x][a][0] * dp[v][b][0] % mod);
}
}
for(int a = 0; a <= size[x] + size[v]; ++ a)
dp[x][a][0] = tmp[a][0], dp[x][a][1] = tmp[a][1];
size[x] += size[v];
}
}
int qpow(int x, int y) {
int tmp = 1;
for(; y ; y >>= 1, x = (ll)x * x % mod) {
if(y & 1) tmp = (ll)tmp * x % mod;
}
return tmp;
}
int get_inv(int x) {
return qpow(x, mod - 2);
}
int calc(int x) {
// x 个对.
// 2 * x 个点, x 个对的方案数.
int tmp = 1, t2 = 1, t3 = 1;
for(int i = 1; i <= 2 * x; ++ i) tmp = (ll)tmp * i % mod;
for(int i = 1; i <= x; ++ i) t2 = (ll)t2 * i % mod;
for(int i = 1; i <= x; ++ i) t3 = (ll)t3 * 2 % mod;
return (ll)tmp * get_inv(t2) % mod * get_inv(t3) % mod;
}
int main() {
// setIO("input");
scanf("%d", &n);
for(int i = 1; i < 2 * n ; ++ i) {
int x, y;
scanf("%d%d", &x, &y);
G[x].pb(y);
G[y].pb(x);
}
dfs(1, 0);
int ans = 0;
for(int i = 0; i <= n; ++ i) {
int d = (i & 1) ? (mod - 1): 1;
ans = ADD(ans, (ll)calc(n - i) * d % mod * ADD(dp[1][i][0], dp[1][i][1]) % mod);
}
printf("%d\n", ans);
return 0;
}
M. String Problem
标签:字符串,后缀自动机
正式赛的时候用一个 $\mathrm{lcp}$ 乱搞切掉的,这里给出正经做法.
遇到字典序问题,考虑利用后缀自动机来解.
静态问题可以沿着后缀自动机的字符边贪心走大的.
那么不妨先维护出前缀 $\mathrm{i}$ 的答案,然后考虑下一位的影响.
显然,如果影响的话一定是下一位的字母被加入答案,那么在后缀自动机中就是答案中深度最小的点改变.
改变成的新点一定是加入 $\mathrm{i+1}$ 字符时所影响的新点,而所有新点总和是 $\mathrm{O(n)}$ 的.
由于后缀自动机的构建方式,在线做是不现实的,不妨离线构建完后缀自动机然后记录每个点最早出现位置.
最后开一个数组统计并离线下来即可.
#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define N 2000009
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
char str[N];
int n, last, tot, fir[N], len[N], st[N], ch[N][26], pre[N], dep[N], vis[N], tail, a[N];
struct data {
int p, c;
data(int p = 0, int c = 0):p(p), c(c){}
};
vector<data>G[N];
void extend(int c, int pos) {
int np = ++ tot, p = last;
len[np] = len[p] + 1, last = np;
fir[np] = pos;
for(; p && !ch[p][c]; p = pre[p]) {
ch[p][c] = np;
}
if(!p) pre[np] = 1;
else {
int q = ch[p][c];
if(len[q] == len[p] + 1) pre[np] = q;
else {
int nq = ++ tot;
len[nq] = len[p] + 1;
fir[nq] = fir[q];
memcpy(ch[nq], ch[q], sizeof(ch[q]));
pre[nq] = pre[q], pre[np] = pre[q] = nq;
for(; p && ch[p][c] == q; p = pre[p])
ch[p][c] = nq;
}
}
}
int main() {
// setIO("input");
scanf("%s", str + 1);
n = strlen(str + 1);
last = tot = 1;
for(int i = 1; i <= n ; ++ i) {
extend(str[i] - 'a', i);
}
for(int i = 1; i <= tot; ++ i) {
for(int j = 0; j < 26; ++ j) {
if(ch[i][j]) {
int q = ch[i][j];
G[fir[q]].pb(data(i, j));
}
}
}
vis[1] = 1, a[++ tail] = 1, st[1] = -1;
for(int i = 1; i <= n ; ++ i) {
int mk = 0, de = 0;
for(int j = 0; j < G[i].size() ; ++ j) {
data e = G[i][j];
if(vis[e.p] && e.c > st[e.p]) {
if(mk == 0) {
mk = e.p, de = dep[e.p];
}
else if(dep[e.p] < de) {
mk = e.p, de = dep[e.p];
}
}
}
if(mk) {
while(a[tail] != mk)
vis[a[tail]] = 0, -- tail;
st[mk] = str[i] - 'a';
a[++ tail] = ch[mk][str[i] - 'a'], dep[a[tail]] = tail, vis[a[tail]] = 1, st[a[tail]] = -1;
}
printf("%d %d\n", i - tail + 2, i);
}
return 0;
}

浙公网安备 33010602011771号