[HNOI/AHOI2018]毒瘤

题目描述

https://www.lydsy.com/JudgeOnline/upload/201804/%E6%B9%96%E5%8D%97%E4%B8%80%E8%AF%95%E8%AF%95%E9%A2%98.pdf

题解

大意:给出一张n个点n+x条边的无向连通图,x很小,求出这个图上最大独立集的方案数。

感觉就是NOIP保卫王国那题的加强版吧。

暴力的话,我们可以考虑在图上随便找一颗生成树,然后把非树边连接的点设置为关键点,然后2^x枚举这些点的选择情况,每次做一遍树形dp就好了。

如果做到这里,那么可以自然而然的想到一个优化:虚树。

就是发现在虚树上的非关键点部分的转移相似,所以我们可以预处理出这些东西来。

我们设f[i][0/1]表示i点的所有不包含关键点的子树的答案。

对于关键点到关键点的转移,我们可以设k[i][0/1][0/1]表示关键点i点到i点的第一个关键点祖先的儿子处i点和那个儿子的选择情况的方案数。

这个直接从i点向上跳,边跳边统计答案就可以算出来了。

注意虚树上的LCA点也要标为关键点。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<vector>
#define N 110009
using namespace std;
typedef long long ll;
const int mod=998244353;
vector<int>vec[N];
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
} 
struct node{int u,v;}ed[50];
ll k[N][2][2],g[N][2],f[N][2],ans;
int tot,head[N],dfn[N],p[19][N],deep[N],a[N],ttt,n,m,st[N],top;
bool vis[N],dy1[N],dy0[N],tag[N];
struct edge{int n,to;}e[N<<1];
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
inline void add2(int u,int v){tag[u]=1;tag[v]=1;vec[u].push_back(v);}////!!!!!!!!!
void dfs(int u,int fa){
    dfn[u]=++dfn[0];vis[u]=1;
    for(int i=1;(1<<i)<=deep[u];++i)p[i][u]=p[i-1][p[i-1][u]];
    for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
        int v=e[i].to;
        if(!vis[v]){deep[v]=deep[u]+1;p[0][v]=u;dfs(v,u);}
        else{a[++a[0]]=u;a[++a[0]]=v;ed[++ttt]=node{u,v};}
    }
}
inline bool cmp(int a,int b){return dfn[a]<dfn[b];}
inline int getlca(int a,int b){
    if(deep[a]<deep[b])swap(a,b);
    for(int i=17;i>=0;--i)if(deep[a]-(1<<i)>=deep[b])a=p[i][a];
    if(a==b)return a;
    for(int i=17;i>=0;--i)if(p[i][a]!=p[i][b])a=p[i][a],b=p[i][b];
    return p[0][a];
}
void predp(int u,int jin){
    f[u][0]=f[u][1]=1;tag[u]=1;
    for(int i=head[u];i;i=e[i].n){
        int v=e[i].to;
        if(p[0][v]!=u||v==jin||tag[v])continue;
        predp(v,jin);
        f[u][0]=f[u][0]*(f[v][0]+f[v][1])%mod;
        f[u][1]=f[u][1]*f[v][0]%mod;
    }
}
inline void getnum(int x,int y){
    k[x][0][0]=k[x][1][1]=1;
    for(int i=x;p[0][i]!=y;i=p[0][i]){
        predp(p[0][i],i);
        ll xx=k[x][0][0],yy=k[x][1][0];
        k[x][0][0]=f[p[0][i]][0]*(k[x][0][1]+k[x][0][0])%mod;
        k[x][0][1]=f[p[0][i]][1]*xx%mod;
        k[x][1][0]=f[p[0][i]][0]*(k[x][1][0]+k[x][1][1])%mod;
        k[x][1][1]=f[p[0][i]][1]*yy%mod;                              
    }
}
void prework(int u){
    for(int i=0;i<vec[u].size();++i){
        int v=vec[u][i];
        prework(v);getnum(v,u);
    }    
    f[u][0]=f[u][1]=1;
    for(int i=head[u];i;i=e[i].n){
        int v=e[i].to;
        if(p[0][v]!=u||tag[v])continue;
        predp(v,0);
        f[u][0]=f[u][0]*(f[v][0]+f[v][1])%mod;
        f[u][1]=f[u][1]*f[v][0]%mod;
    }
}
void dp(int u){
    g[u][0]=f[u][0];g[u][1]=f[u][1];
    if(dy1[u])g[u][0]=0;if(dy0[u])g[u][1]=0;
    for(int i=0;i<vec[u].size();++i){
        int v=vec[u][i];
        dp(v);
        ll k0=(k[v][0][0]*g[v][0]%mod+k[v][1][0]*g[v][1]%mod)%mod,k1=(k[v][0][1]*g[v][0]%mod+k[v][1][1]*g[v][1]%mod)%mod;
        g[u][0]=g[u][0]*(k0+k1)%mod;g[u][1]=g[u][1]*k0%mod;
    }
}
int main(){
    n=rd();m=rd();int u,v;
    for(int i=1;i<=m;++i){
        u=rd();v=rd();
        add(u,v);add(v,u);    
    }
    dfs(1,0);sort(a+1,a+a[0]+1,cmp);
    a[0]=unique(a+1,a+a[0]+1)-a-1;
    st[top=1]=1;
    for(int i=1;i<=a[0];++i){
        tag[a[i]]=1;
        if(a[i]==st[top])continue;
        int lca=getlca(st[top],a[i]);
        if(lca==st[top]){st[++top]=a[i];continue;}
        while(top>1){
            int x=st[top],y=st[top-1];
            if(dfn[lca]>=dfn[y]){add2(lca,x);top--;break;}
            add2(y,x);top--;
        }
        if(st[top]!=lca)st[++top]=lca;
        if(st[top]!=a[i])st[++top]=a[i];
    }
    while(top>1)add2(st[top-1],st[top]),top--;
    prework(1);
    for(int i=0;i<(1<<a[0]);++i){
        for(int j=1;j<=a[0];++j)if(i&(1<<j-1))dy1[a[j]]=1,dy0[a[j]]=0;else dy0[a[j]]=1,dy1[a[j]]=0;
        bool tagg=0;
        for(int j=1;j<=ttt;++j){
            int u=ed[j].u,v=ed[j].v;
            if(dy1[u]&&dy1[v]){tagg=1;break;} 
        }
        if(tagg)continue; 
        dp(1);
        (ans+=g[1][0]+g[1][1])%=mod;
    }
    cout<<ans;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ZH-comld/p/10435484.html