1. 程式人生 > >[bzoj5287] [HNOI2018]毒瘤

[bzoj5287] [HNOI2018]毒瘤

題目描述

從前有一名毒瘤。

毒瘤最近發現了量產毒瘤題的奧祕。考慮如下型別的資料結構題:給出一個數組,要求支援若干種奇奇怪怪的修改操作(比如區間加一個數,或者區間開平方),並支援詢問區間和。毒瘤考慮了n個這樣的修改操作,並編號為\(1\sim n\)。當毒瘤要出資料結構題的時候,他就將這些修改操作中選若干個出來,然後出成一道題。

當然了,這樣出的題有可能不可做。通過精妙的數學推理,毒瘤揭露了這些修改操作的關係:有m對“互相排斥”的修改操作,第i對是第ui個操作和第vi個操作。當一道題同時含有ui和vi這兩個操作時,這道題就會變得不可做。另一方面,一道題中不包含任何“互相排斥”的修改操作時,這個題就是可做的。此外,毒瘤還發現了一個規律:m-n是一個很小的數字,且任意兩個修改操作都是連通的。兩個修改操作a,b是連通的,當且僅當存在若干操作\(t_0,t_1,...,t_l\)

,使得\(t_0=a,t_l=b\),且對1≤i≤l,\(t_{i-1}\)\(t_i\)都是“互相排斥”的修改操作。

一堆“互相排斥”的修改操作稱為互斥對。現在毒瘤想知道,給定值n和m個互斥對,他共能出出多少道可做的不同的資料結構題。兩道資料結構題是不同的,當且僅當有一個修改操作在其中一道題中存在,而在另一道題中不存在。

輸入輸出格式

輸入格式:

第一行為正整數n,m。

接下來m行,每行兩個正整數u,v,代表一對“互相排斥”的修改操作。

輸出格式:

輸出一行一個整數,代表毒瘤可以出的可做的不同的“互相排斥”的修改操作的個數。這個數可能很大,所以只輸出模998244353後的值。

Solution

虛樹。

先處理出一顆生成樹,考慮到非樹邊很少,考慮暴力列舉非樹邊兩端的狀態,複雜度\(O(4^{n-m+1}n)\)

然後優化一下,對於一條非樹邊,兩端的狀態只需要列舉\((1,0)\)\((0,1)\)就好了,\((1,1)\)顯然不合法,\((0,0)\)可以在\(dp\)的時候得到。複雜度\(O(2^{n-m+1}n)\)

考慮到上面的過程列舉時,樹邊是不變的,也就是說,可以考慮把非樹邊所在的點建出一顆虛樹,然後對於虛樹上的點,\(dp\)方程一定可以表示成:
\[ f_{u,0/1}=\prod_{v\in son_u}k_{0/1,0}f_{v,0}+k_{0/1,1}f_{v,1} \]


其中\(k\)為固定的係數,對於虛樹上每條邊,這個轉移都是固定的。

所以可以\(O(n)\)先在原樹\(dp\)出係數,然後暴力列舉關鍵點狀態暴力虛樹上\(dp\)就好了。

複雜度\(O(n+2^{2(m-n+1)}(n-m+1))\),足以通過此題。

#include<bits/stdc++.h>
using namespace std;

#define int long long 

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int mod = 998244353;
const int maxn = 3e5+10;

int n,m,s[100],tt[100],cnt,sz[maxn],dfn[maxn],dep[maxn],use[maxn],val[maxn],vis[maxn],g[maxn][2],pr[maxn];

struct data {int k[2][2];}epsilon;

struct Dsu {
    int fa[maxn];
    void init() {for(int i=1;i<=n;i++) fa[i]=i;}
    int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
}dsu;

struct Input_Tree {
    int head[maxn],tot,f[maxn][20],dfn_cnt;
    struct edge{int to,nxt;}e[maxn<<1];

    void add(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
    void ins(int u,int v) {add(u,v),add(v,u);}

    void dfs(int x,int fa) {
        sz[x]=1,dfn[x]=++dfn_cnt,dep[x]=dep[fa]+1,f[x][0]=fa;
        for(int i=1;i<=19;i++) f[x][i]=f[f[x][i-1]][i-1];
        for(int i=head[x];i;i=e[i].nxt)
            if(e[i].to!=fa) dfs(e[i].to,x),sz[x]+=sz[e[i].to];
    }

    int lca(int x,int y) {
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=19;~i;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
        if(x==y) return x;
        for(int i=19;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        return f[x][0];
    }

    void dp(int x,int fa) {
        int bo=1;g[x][0]=g[x][1]=1;
        for(int i=head[x];i;i=e[i].nxt)
            if(e[i].to!=fa) {
                bo=0,dp(e[i].to,x);int v=e[i].to;
                g[x][0]=1ll*g[x][0]*(g[v][0]+g[v][1])%mod;
                g[x][1]=1ll*g[x][1]*g[v][0]%mod;
            }
        if(bo) g[x][0]=g[x][1]=1;
    }

    int ns[maxn][2],nt[maxn][2];
    
    data get(int x,int fa) {
        ns[x][0]=1,ns[x][1]=0;
        nt[x][0]=0,nt[x][1]=1;
        while(x!=fa) {
            pr[x]=1;
            int pre=x;x=f[x][0];ns[x][0]=ns[x][1]=nt[x][0]=nt[x][1]=1;
            ns[x][0]=1ll*ns[x][0]*(ns[pre][0]+ns[pre][1])%mod;
            ns[x][1]=1ll*ns[x][1]*ns[pre][0]%mod;
            nt[x][0]=1ll*nt[x][0]*(nt[pre][0]+nt[pre][1])%mod;
            nt[x][1]=1ll*nt[x][1]*nt[pre][0]%mod;
            if(x==fa) break;
            for(int i=head[x];i;i=e[i].nxt)
                if(e[i].to!=f[x][0]&&e[i].to!=pre) {
                    int v=e[i].to;
                    ns[x][0]=1ll*ns[x][0]*(g[v][0]+g[v][1])%mod;
                    ns[x][1]=1ll*ns[x][1]*g[v][0]%mod;
                    nt[x][0]=1ll*nt[x][0]*(g[v][0]+g[v][1])%mod;
                    nt[x][1]=1ll*nt[x][1]*g[v][0]%mod;
                }
        }
        data ans;pr[fa]=1;
        ans.k[0][0]=ns[x][0],ans.k[1][0]=ns[x][1];
        ans.k[0][1]=nt[x][0],ans.k[1][1]=nt[x][1];
        return ans;
    }
}T;

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

struct Virtual_Tree {
    int head[maxn],tot;
    struct edge{int to,nxt;data k;}e[maxn<<1];

    void add(int u,int v) {e[++tot]=(edge){v,head[u],epsilon},head[u]=tot;}
    void ins(int u,int v) {add(u,v),add(v,u);}
    
    int in[maxn],k,use[maxn],used,sta[maxn],top,ban[maxn],f[maxn][2],val[maxn][2];

    void build() {
        sort(in+1,in+k+1,cmp);k=unique(in+1,in+k+1)-in-1;
        sta[++top]=1;
        for(int i=1;i<=k;i++) {
            if(in[i]==1) continue;
            int t=T.lca(in[i],sta[top]),pre=-1;
            while(dfn[sta[top]]>dfn[t]&&dfn[sta[top]]<dfn[t]+sz[t]) {
                if(pre!=-1) ins(sta[top],pre);
                pre=sta[top],top--;
            }
            if(pre!=-1) ins(t,pre);
            if(sta[top]!=t) sta[++top]=t;
            sta[++top]=in[i];
        }
        int pre=-1;
        while(top) {
            if(pre!=-1) ins(sta[top],pre);
            pre=sta[top],top--;
        }
    }

    void dfs(int x,int fa) {
        use[x]=1;
        for(int i=head[x];i;i=e[i].nxt)
            if(e[i].to!=fa) dfs(e[i].to,x);
    }

    void make(int x,int fa) {
        for(int i=head[x];i;i=e[i].nxt)
            if(e[i].to!=fa) {
                e[i].k=T.get(e[i].to,x);
                make(e[i].to,x);
            }
    }

    void dp(int x,int fa) {
        f[x][0]=val[x][0],f[x][1]=val[x][1];
        for(int i=head[x];i;i=e[i].nxt)
            if(e[i].to!=fa) {
                dp(e[i].to,x);data u=e[i].k;int v=e[i].to;
                f[x][0]=1ll*f[x][0]*(f[v][0]*u.k[0][0]%mod+f[v][1]*u.k[0][1]%mod)%mod;
                f[x][1]=1ll*f[x][1]*(f[v][0]*u.k[1][0]%mod+f[v][1]*u.k[1][1]%mod)%mod;
            }
        if(ban[x]==0) f[x][1]=0;
        else if(ban[x]==1) f[x][0]=0;
    }
    
    void get_val(int x,int fa) {
        val[x][0]=val[x][1]=1;
        for(int i=T.head[x],v=T.e[i].to;i;i=T.e[i].nxt,v=T.e[i].to)
            if(!pr[v]) {
                val[x][0]=1ll*val[x][0]*(g[v][0]+g[v][1])%mod;
                val[x][1]=1ll*val[x][1]*g[v][0]%mod;
            }
        for(int i=head[x];i;i=e[i].nxt)
            if(e[i].to!=fa) get_val(e[i].to,x);
    }
    
    void solve() 
        for(int i=1;i<=cnt;i++)
            in[++k]=s[i],in[++k]=tt[i],vis[s[i]]=1,vis[tt[i]]=1;
        build();make(1,0);int ans=0;
        memset(ban,-1,sizeof ban);

        get_val(1,0);

        for(int st=0;st<(1<<k);st++) {
            for(int i=1;i<=k;i++)
                if(st&(1<<(i-1))) ban[in[i]]=1;
                else ban[in[i]]=0;
            for(int i=1;i<=cnt;i++)
                if(ban[s[i]]==1&&ban[tt[i]]==1) goto loop;
            dp(1,0);
            ans=(0ll+ans+f[1][0]+f[1][1])%mod;
        loop:;
        }
        write(ans);
    }
}VT;

signed main() {
    read(n),read(m);dsu.init();
    for(int i=1,x,y;i<=m;i++) {
        read(x),read(y);int u=dsu.find(x),v=dsu.find(y);
        if(u==v) s[++cnt]=x,tt[cnt]=y;
        else dsu.fa[u]=v,T.ins(x,y);
    }
    T.dfs(1,0),T.dp(1,0);VT.solve();
    return 0;
}