#虚树,树形dp#洛谷 4426 [HNOI/AHOI2018] 毒瘤

题目传送门


分析

如果只是一棵树的话,就是设 \(dp[x][0/1]\) 表示以 \(x\) 为根的子树不选/选点 \(x\) 的独立集个数。

那么 \(dp[x][0]=\prod (dp[y][0]+dp[y][1]),dp[x][1]=\prod dp[y][0]\)

但存在非树边的话,就存在三种情况,也就是两个端点不都选,就可以转化成一个端点不选另一个端点必选和另一个端点不选两种情况。

直接树形dp就是 \(n2^{m-n+1}\),考虑对于这 \(2(m-n+1)\) 个点建虚树,预处理虚树节点间的系数贡献关系,

虚树上的点用不包含非树边节点的子树进行贡献,主要是系数贡献比较难写,这样就把指数的部分的系数变成 \(m\)


代码

#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
const int N=200011,mod=998244353; struct node{int y,next;}e[N<<1],E[N]; long long g[2][N][2],ans;
int dep[N],fat[N],siz[N],Siz[N],big[N],dfn[N],Top[N],vst[N],vtop,Et,hs[N],as[N],n,m,k,tot,a[N],X[N],Y[N],dp[N][2],f[N][2],et=1;
void dfs1(int x,int fa){
	dep[x]=dep[fa]+1,fat[x]=fa,siz[x]=1;
	for (int i=as[x],SIZ=-1;i;i=e[i].next)
	if (!dep[e[i].y]){
		dfs1(e[i].y,x),siz[x]+=siz[e[i].y];
		if (SIZ<siz[e[i].y]) big[x]=e[i].y,SIZ=siz[e[i].y];
	}else if (e[i].y!=fa) Siz[x]=Siz[e[i].y]=1;
}
void dfs2(int x,int linp){
	dfn[x]=++tot,Top[x]=linp;
	if (!big[x]) return;
    dfs2(big[x],linp),Siz[x]+=Siz[big[x]];
	for (int i=as[x];i;i=e[i].next)
	if (e[i].y!=big[x]&&fat[e[i].y]==x)
	    dfs2(e[i].y,e[i].y),Siz[x]+=Siz[e[i].y];
}
int lca(int x,int y){
	while (Top[x]!=Top[y]){
		if (dep[Top[x]]<dep[Top[y]]) x^=y,y^=x,x^=y;
		x=fat[Top[x]];
	}
	if (dep[x]>dep[y]) x^=y,y^=x,x^=y;
	return x;
}
bool cmp(int x,int y){return dfn[x]<dfn[y];}
void add(int x,int y){E[++Et]=(node){y,hs[x]},hs[x]=Et;}
void Insert(int x){
	if (!vtop) {vst[++vtop]=x; return;}
	int Lca=lca(x,vst[vtop]);
	for (;vtop>1&&dep[Lca]<dep[vst[vtop-1]];--vtop)
        add(vst[vtop-1],vst[vtop]);
	if (dep[Lca]<dep[vst[vtop]]) add(Lca,vst[vtop--]);
	if (vst[vtop]!=Lca) vst[++vtop]=Lca;
    vst[++vtop]=x;
}
void DP(int x,int fa){
    dp[x][0]=dp[x][1]=1;
    for (int i=as[x];i;i=e[i].next)
    if (fat[e[i].y]==x&&e[i].y!=fa){
        DP(e[i].y,x);
        dp[x][0]=1ll*(dp[e[i].y][0]+dp[e[i].y][1])*dp[x][0]%mod;
        dp[x][1]=1ll*dp[x][1]*dp[e[i].y][0]%mod;
    }
}
void DP(int x){
    f[x][0]=f[x][1]=1;
    for (int i=as[x];i;i=e[i].next)
    if (fat[e[i].y]==x&&!Siz[e[i].y]){
        DP(e[i].y);
        f[x][0]=1ll*(f[e[i].y][0]+f[e[i].y][1])*f[x][0]%mod;
        f[x][1]=1ll*f[x][1]*f[e[i].y][0]%mod;
    }
}
void DFS(int x,int fa){
    g[0][x][0]=g[0][x][1]=g[1][x][0]=1;
    for (int y=x;fat[y]!=fa;y=fat[y]){
        DP(fat[y],y);
        long long g00=g[0][x][0],g01=g[0][x][1];
        g[0][x][0]=(dp[fat[y]][0]*g00+dp[fat[y]][1]*g[1][x][0])%mod;
        g[0][x][1]=(dp[fat[y]][0]*g01+dp[fat[y]][1]*g[1][x][1])%mod;
        g[1][x][0]=dp[fat[y]][0]*g00%mod;
        g[1][x][1]=dp[fat[y]][0]*g01%mod;
    }
}
void dfs(int x){
    a[++m]=x;
    for (int i=hs[x];i;i=E[i].next)
        DP(E[i].y),DFS(E[i].y,x),dfs(E[i].y);
}
void Dp(int x){
    for (int i=hs[x];i;i=E[i].next){
        Dp(E[i].y);
        dp[x][0]=(g[0][E[i].y][0]*dp[E[i].y][0]+g[0][E[i].y][1]*dp[E[i].y][1])%mod*dp[x][0]%mod;
        dp[x][1]=(g[1][E[i].y][0]*dp[E[i].y][0]+g[1][E[i].y][1]*dp[E[i].y][1])%mod*dp[x][1]%mod;
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for (int i=1;i<=m;++i){
        int x,y; cin>>x>>y;
        e[++et]=(node){y,as[x]},as[x]=et;
        e[++et]=(node){x,as[y]},as[y]=et;
    }
	dfs1(1,0),dfs2(1,1);
    for (int x=1;x<=n;++x)
    for (int i=as[x];i;i=e[i].next)
    if (fat[x]!=e[i].y&&fat[e[i].y]!=x&&dfn[x]<dfn[e[i].y])
        X[k]=x,Y[k++]=e[i].y,a[++m]=x,a[++m]=e[i].y;
    a[++m]=1,sort(a+1,a+1+m,cmp),m=unique(a+1,a+1+m)-a-1;
    for (int i=1;i<=m;++i) Insert(a[i]);
    for (;vtop>1;--vtop) add(vst[vtop-1],vst[vtop]);
    m=0,DP(1),dfs(1);
    for (int i=0;i<(1<<k);++i){
        for (int j=1;j<=m;++j) dp[a[j]][0]=f[a[j]][0],dp[a[j]][1]=f[a[j]][1];
        for (int j=0;j<k;++j)
        if ((i>>j)&1) dp[X[j]][0]=dp[Y[j]][1]=0;
            else dp[X[j]][1]=0;
        Dp(1),ans+=dp[1][0]+dp[1][1];
    }
    cout<<ans%mod;
	return 0;
}
posted @ 2025-08-21 22:05  lemondinosaur  阅读(5)  评论(0)    收藏  举报