bzoj5287: [Hnoi2018]毒瘤【虚树+树形dp】

传送门

解题思路:

首先如果图是一棵树,那么:
f [ u ] [ 0 ] = ( f [ v ] [ 0 ] + f [ v ] [ 1 ] ) f [ u ] [ 1 ] = f [ v ] [ 0 ]

如果多了几条非树边,可以暴力枚举这几条边端点的状态,即强制选A不选B或强制不选A,所以得到了 O ( 2 m n + 1 n ) 的算法,可过75分。

考虑把有非树边的点当做关键点建虚树,那么对于虚树上的一条边,它的转移系数其实是不变的,即

f [ u ] [ 0 / 1 ] = ( k [ u ] [ 0 / 1 ] [ 0 ] f [ v ] [ 0 ] + f [ u ] [ 0 / 1 ] [ 1 ] f [ v ] [ 1 ] )

如果我们求出了每条边的转移系数,那么最后就可以向暴力中枚举关键点状态来确定初始值dp即可,求法如下。

对于一条虚树边 u v 将原树中的路径提出来
从节点 v 向上跳并暴力转移系数,若遇到分叉则额外转移上分叉处的系数
如果遇到一个新的关键点就重置系数。
这样复杂度是 O ( n ) 的,可以在建虚树的同时处理。

所以复杂度为 O ( n + k 2 k ) k 为虚树大小。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=100010,mod=998244353;
int n,m,cnt,idx,eu[N],ev[N],dfn[N],vis[N],size[N],mark[N],fg[N][2];
vector<int>e[N];
ll g[N][2],f[N][2],ans;
struct data
{
    ll x,y;
    data(ll _x=0,ll _y=0):x(_x),y(_y){}
    inline data operator + (const data b){return data((x+b.x)%mod,(y+b.y)%mod);}
    inline data operator * (const int b){return data(x*b%mod,y*b%mod);}
}k[N][2],f0[N],f1[N];
int tot,first[N],nxt[N],to[N];
void dfs(int u,int fa)
{
    dfn[u]=++idx;
    for(int i=0;i<e[u].size();i++)
    {
        int v=e[u][i];if(v==fa)continue;
        if(dfn[v]&&dfn[v]<dfn[u])mark[u]=mark[v]=1,eu[++cnt]=u,ev[cnt]=v;
        if(!dfn[v])dfs(v,u),size[u]+=size[v];
    }
    mark[u]|=(size[u]>=2),size[u]=mark[u]?1:size[u];
}
void add(int x,int y,data a,data b)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y,f0[tot]=a,f1[tot]=b;
}
int Vt(int u)
{
    g[u][0]=g[u][1]=1,vis[u]=1;
    int pos=0;
    for(int i=0;i<e[u].size();i++)
    {
        int v=e[u][i];if(vis[v])continue;
        int w=Vt(v);
        if(!w)g[u][0]=g[u][0]*(g[v][0]+g[v][1])%mod,g[u][1]=g[u][1]*g[v][0]%mod;
        else
        {
            if(mark[u])add(u,w,k[v][0]+k[v][1],k[v][0]);
            else k[u][0]=k[v][0]+k[v][1],k[u][1]=k[v][0],pos=w;
        }
    }
    if(mark[u])k[u][0]=data(1,0),k[u][1]=data(0,1),pos=u;
    else k[u][0]=k[u][0]*g[u][0],k[u][1]=k[u][1]*g[u][1];
    return pos;
}
void dp(int u)
{
    f[u][0]=fg[u][1]?0:g[u][0];
    f[u][1]=fg[u][0]?0:g[u][1];
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];dp(v);
        f[u][0]=f[u][0]*(f0[e].x*f[v][0]%mod+f0[e].y*f[v][1]%mod)%mod;
        f[u][1]=f[u][1]*(f1[e].x*f[v][0]%mod+f1[e].y*f[v][1]%mod)%mod;
    }
}
int main()
{
    //freopen("lx.in","r",stdin);
    int x,y;
    n=getint(),m=getint();
    while(m--)x=getint(),y=getint(),e[x].pb(y),e[y].pb(x);
    dfs(1,0),mark[1]=1,Vt(1);
    for(int s=0;s<(1<<cnt);s++)
    {
        for(int i=1;i<=cnt;i++)
            if(s&(1<<i-1))fg[eu[i]][1]=fg[ev[i]][0]=1;
            else fg[eu[i]][0]=1;
        dp(1),ans=(ans+f[1][1]+f[1][0])%mod;
        for(int i=1;i<=cnt;i++)
            if(s&(1<<i-1))fg[eu[i]][1]=fg[ev[i]][0]=0;
            else fg[eu[i]][0]=0;
    }
    cout<<ans<<'\n';
}

猜你喜欢

转载自blog.csdn.net/cdsszjj/article/details/80249632