2024杭电多校6——1007树上MEX问题

补题点这里

在这里插入图片描述
在这里插入图片描述
做一些对官方题解的补充:
1.为什么问题可以转化为求 M E X ≥ i MEX \geq i MEXi的连通块数量,
题目让我们求所有连通导出子图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=1niMEX=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=1nisumi,

∣ M E X ≥ i ∣ = ∑ j = i n s u m j |MEX\geq i|=\sum_{j=i}^{n} sum_j MEXi=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=1nMEXi=i=1nj=insumj
这个式子换个形式就是 ∑ i = 1 n i ∗ s u m i \sum_{i=1}^{n} i*sum_i i=1nisumi,也就是要求的答案,(如果不理解的话带个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的情况,题解里给了方法,但是我不会实现,有会的佬教教我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值