做一些对官方题解的补充:
1.为什么问题可以转化为求
M
E
X
≥
i
MEX \geq i
MEX≥i的连通块数量,
题目让我们求所有连通导出子图MEX之和,可能存在的答案有1,2…n,对于
i
i
i,它的贡献是
M
E
X
=
i
MEX=i
MEX=i的联通子图数量乘上MEX,
所以答案
a
n
s
=
∑
i
=
1
n
i
∗
∣
M
E
X
=
i
∣
ans=\sum_{i=1}^{n} i*|MEX=i|
ans=∑i=1ni∗∣MEX=i∣,
记
s
u
m
i
=
∣
M
E
X
=
i
∣
sum_i=|MEX=i|
sumi=∣MEX=i∣ ,
所以
a
n
s
=
∑
i
=
1
n
i
∗
s
u
m
i
ans = \sum_{i=1}^{n} i*sum_i
ans=∑i=1ni∗sumi,
而
∣
M
E
X
≥
i
∣
=
∑
j
=
i
n
s
u
m
j
|MEX\geq i|=\sum_{j=i}^{n} sum_j
∣MEX≥i∣=∑j=insumj
∑
i
=
1
n
∣
M
E
X
≥
i
∣
=
∑
i
=
1
n
∑
j
=
i
n
s
u
m
j
\sum_{i=1}^{n} |MEX\geq i|=\sum_{i=1}^{n}\sum_{j=i}^{n} sum_j
∑i=1n∣MEX≥i∣=∑i=1n∑j=insumj
这个式子换个形式就是
∑
i
=
1
n
i
∗
s
u
m
i
\sum_{i=1}^{n} i*sum_i
∑i=1ni∗sumi,也就是要求的答案,(如果不理解的话带个n进去,自己展开写一下就明白了)
至于题解里的联想我是不明白,我只能从结果说明二者等价
2.对于求连通块的的dp
只用本身也是一个连通块,所以dp初值是1
为什么是
∏
(
1
+
d
p
[
v
]
)
∏(1+dp[v])
∏(1+dp[v]), 1代表不选这棵子树,dp[v]是选择这棵子树的方案数,子树独立,所以乘起来即可
3.为什么
n
u
m
i
num_i
numi是和集合
S
i
S_i
Si有直接连边但不在集合里的点v,它们的
(
1
+
d
p
[
v
]
)
(1+dp[v])
(1+dp[v])乘起来?
类似于求连通块的dp,相当于把集合
S
i
S_i
Si缩成一个点,那么这样看不就是求这个点的dp值吗
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
const int mod = 998244353;
const int maxn = 1e5+10;
//代码没有考虑(1+dp[x])%mod==0的情况
i64 qp(i64 a,i64 b){
i64 res = 1;
while(b){
if(b&1) res = (res*a)%mod;
b>>=1;
a = (a*a)%mod;
}
return res;
}
int n,ff[maxn],rt,ans,now;
int fa[maxn];
void dfs(int x,int fat,vector<int>&dp,vector<vector<int>> &g){
fa[x] = fat;
for(auto y:g[x]){
if(y==fat) continue;
dfs(y,x,dp,g);
dp[x] = (dp[x]*(1LL+dp[y])%mod)%mod;
}
}
void solve() {
cin>>n;
vector<int>a(n+1);
vector<vector<int>> g(n+1);
for(int i = 1;i<=n;++i){
cin>>a[i];
ff[a[i]] = i;
}
for(int i = 1;i<n;++i){
int x,y;
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
}
vector<int> dp(n+1,1);
rt = ff[0];
dfs(rt,0,dp,g);
ans = dp[rt],now = ans;
stack<int>st;
vector<bool> vis(n+1,0);
vis[rt]=1;
for(int i = 1;i<n;++i){
int u = ff[i];
if(!vis[u]){
while(!vis[u]){
st.push(u);
u = fa[u];
}
while(!st.empty()){
int x = st.top();
st.pop();
vis[x]=1;
now = (1LL*now*(qp((1LL+dp[x])%mod,mod-2)%mod))%mod;
now = (1LL*now*dp[x])%mod;
}
}
ans = (1LL*ans+now)%mod;
}
cout<<ans<<"\n";
}
signed main() {
ios;
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
对于处理 ( 1 + d p [ v ] ) % m o d = = 0 (1+dp[v])\%mod==0 (1+dp[v])%mod==0的情况,题解里给了方法,但是我不会实现,有会的佬教教我