1. 程式人生 > >Bzoj_5287 [Hnoi2018]毒瘤

Bzoj_5287 [Hnoi2018]毒瘤

題意
給你一個圖,求這個圖的獨立點集(集合內點互不相鄰)個數。
\(n \leqslant 10^5,n-1 \leqslant m \leqslant n+10\)

題解
對於m=n-1的情況,直接上Tree_Dp即可,\(f_{u,1}=\prod (f_{v,0}+f_{v,1}),f_{u,0}=\prod f_{v,1}\)
然後我們發現m-n是個很小的數字,我們考慮可以先建出一個生成樹,再考慮那麼非樹邊。
非樹邊最多11條,我們可以直接列舉非樹邊兩端點選或不選(實際上可以只列舉一端)。然後在樹上直接Dp,複雜度\(O(2^{m-n+1}n)\)然後你就有75分了

嘗試對這個演算法進行優化。我們發現每次Dp都有很多重複的步驟(比如完全沒有建在虛樹裡的子樹),我們可以提前預處理。但是在虛樹邊上的點我們還是得一個一個列舉。但是我們又發現,對於每一個虛樹邊<u,v>,\(f_{v,1}\)

\(f_{u,0}\)的貢獻只需要乘上一個固定的係數。具體來說,我們設\(f_{v,1}=x\),那麼點v在虛樹上暴力向上跳,並將周圍的子樹的貢獻也乘進去,最終對\(f_{u,0}\)的貢獻一定是乘上\(k_{<u,v>,0,1} \cdot x\)。所以我們對於每一個虛樹邊<u,v>以及u,v選或不選的每一種狀態都預處理裡出這個係數,然後列舉狀態並在虛樹上Dp,這樣複雜度便是\(O(n+s \times 2^s)\)(s為非樹邊兩端點的點集大小)。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5;
const int mod=998244353;
int n,m,tot,cnt,Time,k;
int a[maxn+8],b[maxn+8],fa[maxn+8];
int pre[maxn*2+8],now[maxn+8],son[maxn*2+8];
int jump[maxn*2+8][28],dfn[maxn+8],dep[maxn+8];
int tmp[2],g[maxn+8][2],res[maxn+8][2],val[maxn*2+8][2][2];
int st[maxn+8];
int f[maxn+8][2];
bool vis[maxn+8];

int read()
{
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}

struct Disjoint_Set_Union
{
    int fa[maxn+8];
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
}DSU;

void add(int u,int v)
{
    pre[++tot]=now[u];
    now[u]=tot;
    son[tot]=v;
}

void dfs(int x)
{
    jump[dfn[x]=++Time][0]=x;
    dep[x]=dep[fa[x]]+1;
    for (int p=now[x];p;p=pre[p])
        {
            int child=son[p];
            if (child==fa[x]) continue;
            fa[child]=x;
            dfs(child);
            jump[++Time][0]=x;
        }
}

int RMQ(int x,int y){return dep[x]<dep[y]?x:y;}

void Build_St()
{
    for (int j=1;j<=log(Time)/log(2);j++)
        for (int i=1;i<=Time-(1<<j)+1;i++)
            jump[i][j]=RMQ(jump[i][j-1],jump[i+(1<<(j-1))][j-1]);
}

bool cmp(int x,int y){return dfn[x]<dfn[y];}

int Get_Lca(int x,int y)
{
    if (dfn[x]>dfn[y]) swap(x,y);
    int len=dfn[y]-dfn[x]+1,t=log(len)/log(2);
    return RMQ(jump[dfn[x]][t],jump[dfn[y]-(1<<t)+1][t]);
}

void Make_F(int x,int fa)
{
    f[x][0]=1,f[x][1]=1;
    for (int p=now[x];p;p=pre[p])
        {
            int child=son[p];
            if (child==fa) continue;
            Make_F(child,x);
            f[x][1]=1ll*f[x][1]*f[child][0]%mod;
            f[x][0]=1ll*(f[child][1]+f[child][0])%mod*f[x][0]%mod;
        }
}

int update(int x,int y,int i,int j)
{
    tmp[i^1]=0,tmp[i]=1;
    while(x!=y)
        {
            vis[x]=1;
            for (int p=now[x];p;p=pre[p])
                {
                    int child=son[p];
                    if (vis[child]||child==fa[x]) continue;
                    tmp[0]=1ll*(f[child][0]+f[child][1])%mod*tmp[0]%mod;
                    tmp[1]=1ll*tmp[1]*f[child][0]%mod;
                }
            swap(tmp[0],tmp[1]);
            tmp[0]=(tmp[0]+tmp[1])%mod;
            x=fa[x];
        }
    return tmp[j];
}

void Get_Res(int x)
{
    res[x][0]=1,res[x][1]=1;
    for (int p=now[x];p;p=pre[p])
        {
            int child=son[p];
            if (vis[child]||child==fa[x]) continue;
            vis[child]=1;
            res[x][0]=1ll*(f[child][0]+f[child][1])%mod*res[x][0]%mod;
            res[x][1]=1ll*res[x][1]*f[child][0]%mod;
        }
}

struct Virtual_Tree
{
    int tot;
    int color[maxn+8];
    int pre[maxn*2+8],now[maxn+8],son[maxn*2+8];
    void add(int u,int v)
    {
        pre[++tot]=now[u];
        now[u]=tot;
        son[tot]=v;
    }
    void insert(int u,int v){add(u,v),add(v,u);}
    void Make_Val(int x,int fa)
    {
        for (int p=now[x];p;p=pre[p])
            {
                int child=son[p];
                if (child==fa) continue;
                Make_Val(child,x);
                for (int i=0;i<2;i++)
                    for (int j=0;j<2;j++)
                            val[p][i][j]=update(child,x,i,j);
            }
        Get_Res(x);
    }
    void calc(int x,int fa)
    {
        if (color[x]==-1) g[x][0]=res[x][0],g[x][1]=res[x][1];
        else g[x][color[x]]=res[x][color[x]],g[x][color[x]^1]=0;
        for (int p=now[x];p;p=pre[p])
            {
                int child=son[p];
                if (child==fa) continue;
                calc(child,x);
                for (int i=0;i<2;i++)
                    g[x][i]=1ll*(1ll*g[child][0]*val[p][0][i]%mod+1ll*g[child][1]*val[p][1][i]%mod)*g[x][i]%mod;
            }
    }
    void solve()
    {
        int ans=0;
        Make_Val(n,0);
        memset(color,-1,sizeof(color));
        for (int sta=0;sta<1<<k;sta++)
            {
                for (int i=1;i<=k;i++) color[a[i]]=1&(sta>>(i-1));
                bool flag=0;
                for (int i=1;i<=cnt;i+=2)
                    if (color[b[i]]&color[b[i+1]])
                        {
                            flag=1;
                            break;
                        }
                if (flag) continue;
                calc(n,0);
                ans=(ans+g[n][0])%mod;
            }
        printf("%d\n",ans);
    }
}VT;

void solve()
{
    sort(a+1,a+cnt+1,cmp);
    k=unique(a+1,a+cnt+1)-a-1;
    int tail=1;
    st[tail]=n;
    for (int i=1;i<=k;i++)
        {
            int Lca=Get_Lca(a[i],st[tail]),pre=0;
            while(dep[Lca]<dep[st[tail]])
                {
                    if (pre) VT.insert(pre,st[tail]);
                    pre=st[tail--];
                }
            if (pre) VT.insert(pre,Lca);
            if (Lca!=st[tail]) st[++tail]=Lca;
            st[++tail]=a[i];
        }
    tail--;
    while(tail) VT.insert(st[tail],st[tail+1]),tail--;
    VT.solve();
}

int main()
{
    n=read(),m=read();
    for (int i=1;i<=n;i++) DSU.fa[i]=i;
    for (int i=1;i<=m;i++)
        {
            int u=read(),v=read();
            if (DSU.find(u)!=DSU.find(v))
                {
                    add(u,v),add(v,u);
                    DSU.fa[DSU.find(u)]=DSU.find(v);
                }
            else
                {
                    b[cnt+1]=u,b[cnt+2]=v;
                    a[++cnt]=u,a[++cnt]=v;
                }
        }
    ++n;
    add(1,n),add(n,1);
    dfs(n);
    Build_St();
    Make_F(n,0);
    solve();
    return 0;
}