洛谷P4426/LOJ2496/BZOJ5287[HNOI2018/AHOI2018]毒瘤(动态规划+虚树)

求一个联通图的独立集个数(包括空集)

对于树的情况,记 $dp_{u,0/1}$ 为以 $u$ 为根的子树在不选/选 $u$ 时的独立集数量,转移方程是显然的:

$$dp_{u,0}=\prod\limits_{v\in son_u}(dp_{v,0}+dp_{v,1})$$

$$dp_{u,1}=\prod\limits_{v\in son_u}dp_{v,0}$$

注意到非树边很少,所以可以暴力枚举每条边上某个点选/不选,再钦点另一个点的状态,重新算出 DP 值,加起来就可以了。

同时,每次钦点只对该点及其祖先产生影响,所以把这些点拎出来建虚树,虚树上的转移用 DDP 跳链时候的方法维护就可以了。

时间复杂度 $O(nlogn+2^{m-n}(m-n))$。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define For(i,A,B) for(i=(A);i<=(B);++i)
#define fi first
#define se second
using namespace std;
typedef long long ll;
const int mod=998244353;
const int N=100050;
const int BUF=1<<21;
char rB[BUF],*rS,*rT;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,BUF,stdin),rS==rT)?EOF:*rS++;}
inline int rd(){
    char c=gc();
    while(c<48||c>57)c=gc();
    int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
int G[N],to[N<<1],nxt[N<<1],sz,g[N],f[18][N],dep[N],dfn[N],dfsc,pa[N],rnk[N],dp[N][2],F[N][2],h[N][2],st[45],sl=-1,r[25];
bool tag[N][2],b[N];
pair<int,int> E[15];
struct mat{
    int A[2][2];
    mat(){}
    mat(int a,int b,int c,int d){A[0][0]=a;A[0][1]=b;A[1][0]=c;A[1][1]=d;}
    inline mat operator*(const mat &b)const{return mat(((ll)A[0][0]*b.A[0][0]+(ll)A[0][1]*b.A[1][0])%mod,((ll)A[0][0]*b.A[0][1]+(ll)A[0][1]*b.A[1][1])%mod,((ll)A[1][0]*b.A[0][0]+(ll)A[1][1]*b.A[1][0])%mod,((ll)A[1][0]*b.A[0][1]+(ll)A[1][1]*b.A[1][1])%mod);}
}w[N];
inline bool cmp(int x,int y){return dfn[x]<dfn[y];}
int gf(int x){return pa[x]==x?x:pa[x]=gf(pa[x]);}
inline void uni(int x,int y){
    if(rnk[x]<rnk[y])pa[x]=y;
    else{
        if(rnk[x]==rnk[y])++rnk[x];
        pa[y]=x;
    }
}
inline void adde(int u,int v){
    to[++sz]=v;nxt[sz]=G[u];G[u]=sz;
    to[++sz]=u;nxt[sz]=G[v];G[v]=sz;
}
void dfs(int u,int fa){
    int i,v;
    dep[u]=dep[f[0][u]=fa]+1;
    for(i=1;(1<<i)<dep[u];++i)f[i][u]=f[i-1][f[i-1][u]];
    dfn[u]=++dfsc;
    dp[u][0]=dp[u][1]=1;
    for(i=G[u];i;i=nxt[i])if((v=to[i])!=fa){
        dfs(v,u);
        dp[u][0]=(ll)dp[u][0]*(dp[v][0]+dp[v][1])%mod;
        dp[u][1]=(ll)dp[u][1]*dp[v][0]%mod;
    }
}
inline int lca(int u,int v){
    short i;
    if(dep[u]<dep[v])swap(u,v);
    for(i=16;i>=0;--i)if(dep[u]-(1<<i)>=dep[v])u=f[i][u];
    if(u==v)return u;
    for(i=16;i>=0;--i)if(f[i][u]!=f[i][v]){u=f[i][u];v=f[i][v];}
    return f[0][u];
}
inline void linke(int u,int v){
    int fa,i,tp,t1,t2,tv=v;
    to[++sz]=v;nxt[sz]=g[u];g[u]=sz;
    w[v]=mat(1,0,0,1);
    for(;(fa=f[0][tv])!=u;tv=fa){
        for(i=G[fa],t1=t2=1;i;i=nxt[i])if((tp=to[i])!=f[0][fa]&&tp!=tv){t1=(ll)t1*(dp[tp][0]+dp[tp][1])%mod;t2=(ll)t2*dp[tp][0]%mod;}
        w[v]=mat(t1,t1,t2,0)*w[v];
    }
    b[tv]=1;
}
inline void ins(int u){
    int l=lca(st[sl],u);
    if(l!=st[sl]){
        for(;sl&&dep[st[sl-1]]>=dep[l];--sl)linke(st[sl-1],st[sl]);
        if(st[sl]!=l){
            linke(l,st[sl]);
            st[sl]=l;
        }
    }
    st[++sl]=u;
}
void Dfs(int u){
    int i,v;
    h[u][0]=h[u][1]=1;
    for(i=g[u];i;i=nxt[i])Dfs(to[i]);
    for(i=G[u];i;i=nxt[i])if((v=to[i])!=f[0][u]&&!b[v]){h[u][0]=(ll)h[u][0]*(dp[v][0]+dp[v][1])%mod;h[u][1]=(ll)h[u][1]*dp[v][0]%mod;}
}
void Dp(int u){
    int i,v,s0=tag[u][0]?0:h[u][0],s1=tag[u][1]?0:h[u][1];
    for(i=g[u];i;i=nxt[i]){
        Dp(v=to[i]);
        s0=(ll)s0*(F[v][0]+F[v][1])%mod;s1=(ll)s1*F[v][0]%mod;
    }
    F[u][0]=((ll)s0*w[u].A[0][0]+(ll)s1*w[u].A[0][1])%mod;
    F[u][1]=((ll)s0*w[u].A[1][0]+(ll)s1*w[u].A[1][1])%mod;
}
int main(){
    int n=rd(),m=rd(),i,u,v,x,y,S,cnt=0,tot=0,ans=0;
    For(i,1,n)pa[i]=i;
    For(i,1,m){
        x=gf(u=rd());y=gf(v=rd());
        if(x!=y){
            adde(u,v);
            uni(x,y);
        }else{
            E[cnt].fi=u;E[cnt++].se=v;
            r[tot++]=u;r[tot++]=v;
        }
    }
    dfs(1,0);
    if(!cnt){
        printf("%d",(dp[1][0]+dp[1][1])%mod);
        return 0;
    }
    sort(r,r+tot,cmp);tot=unique(r,r+tot)-r;
    st[sl=0]=1;
    if(r[0]!=1)ins(r[0]);
    For(i,1,tot-1)ins(r[i]);
    for(;sl;--sl)linke(st[sl-1],st[sl]);
    w[1].A[0][0]=w[1].A[1][1]=1;
    Dfs(1);
    For(S,0,(1<<cnt)-1){
        For(i,0,cnt-1)if((S>>i)&1){tag[E[i].fi][0]=1;tag[E[i].se][1]=1;}
        else tag[E[i].fi][1]=1;
        Dp(1);
        ans=((ll)ans+F[1][0]+F[1][1])%mod;
        For(i,0,cnt-1)tag[E[i].fi][0]=tag[E[i].fi][1]=tag[E[i].se][0]=tag[E[i].se][1]=0;
    }
    printf("%d",ans);
    return 0;
}
View Code

 

posted @ 2020-03-29 21:33  wangyuchen  阅读(206)  评论(0编辑  收藏  举报