水题选讲

duliu

点就完了
因为标签是虚树所以考虑树
看到5~6的数据点,发现图是棵树,于是快乐地把5~6数据点作为切入点
其实就是么有上司的舞会是吧

\[f[x][0]=Σf[v][0]+f[v][1] \]

\[f[x][1]=Σf[v][0] \]

那么我们对于一个图就直接地把图破成树好了,枚举每条边是否选择,强行跑树DP,但是边太多了所以必死无疑
但是考虑标签是虚树但是看到数据,\(m\)只比\(n\)\(22\),通过虚树可以让枚举次数大大降低,另外,因为每次只会有一些特殊的点(就是环上的点)对答案做出贡献,可以用虚树来优化
而最大的\(m \leq n+11\),我们可以把这些多出来的点拎出来构建虚树,并在虚树上DP

注意,接下来是本题最恶心的地方

我们假设有下面这条链(这条链将被用来建立虚树,我们会在这条边上DP)
graph.png
dp数组向上传递是有一定规律的

\[f[5][0]=πf[6][0]+f[6][1] \]

\[f[5][1]=πf[6][0] \]

\[f[4][0]=πf[5][0]+f[5][1] \]

\[即 \]

\[f[4][0]=π(f[6][0]+f[6][1])+f[6][0] \]

\[f[4][1]=πf[6][0]+f[6][1] \]

\[f[3][0]=πf[4][0]+f[4][1] \]

\[f[3][0]=π((f[6][0]+f[6][1])+f[6][0])+(f[6][0]+f[6][1]) \]

\[f[3][1]=πf[4][0] \]

\[f[3][1]=πf[6][0]+f[6][1]+f[6][0] \]

我们发现

\[f[6][0] 和 f[6][1] \]

每次递归会传递一次这玩意,而虚树边上的点是固定的,所以这两个玩意被加上的次数是可以递归处理出来的
所以我们得到这个方程(v是x在虚树上的父亲)

\[f[x][1]=k0[v][1]*f[v][0]+k1[v][1]*f[v][1] \]

\[f[x][0]=k0[v][0]*f[v][0]+k1[v][0]*f[v][0] \]

预处理出k就能舒服DP
而预处理的方法比较坑
我们先考虑x原树上的一个儿子t
设在添加t(树DP的时候)前\(f[x][1]\)\(ovo[x][1]\)

\[f[x][1]=ovo[x][1]*[(f[v][0]*k1[v][0])+(f[v][1]*k0[v][1])] \]

所以考虑刚才对k数组的理解

\[k1[v][0]=ovo[x][1]*k0[v][0] \]

最后梳理下dp的流程

预处理出k数组
每次暴力枚举加入了哪些非树边
树DP

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 100020 
#define mod 998244353
using namespace std;
ll head[N],hh[N],dfn[N],dep[N],f[25][N],ff[N][3],k0[N][3],k1[N][3],dp[N][3];
ll b[25],bj[N],in[N],g[N][3],p[N],st[N];
ll n,m,cnt=1,ret,tmp,T,tt,tp,ans;
bool vis[N],en[N*2];

struct Tuck{
      ll u1,v1;
}tr[N];

struct The_World{
      ll to,nxt;
}q[N*10];

struct Star_Platinum{
      ll v,nx;
}e[N*10];

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

void add(ll u,ll v){
      q[++cnt].to=v,q[cnt].nxt=head[u];
      head[u]=cnt;
      q[++cnt].to=u,q[cnt].nxt=head[v];
      head[v]=cnt;
}

void edd(ll u,ll v){
	e[++ret].v=v,e[ret].nx=hh[u];
	hh[u]=ret;
}

void dfs(ll x,ll fa){
      dfn[x]=++tmp;
      dep[x]=dep[fa]+1;
      vis[x]=1;
      for(ll i=1;i<=20&&f[i-1][x];i++){
            f[i][x]=f[i-1][f[i-1][x]];
      }
      for(ll i=head[x];i;i=q[i].nxt){
            ll v=q[i].to;
            if(v!=fa){
                  if(!vis[v]){
                        f[0][v]=x;
                        dfs(v,x);
                  }
                  else {
                        T++;
                        tr[T].u1=x,tr[T].v1=v;
                        en[i]=en[i^1]=1;
                  }
            }
      }
}

ll LYCA(ll x,ll y){
      if(dep[x]<dep[y])swap(x,y);
      for(ll i=20;i>=0;i--){
            if(dep[f[i][x]]>=dep[y]){
                  x=f[i][x];
            }
            if(x==y)return x;
      }
      for(ll i=20;i>=0;i--){
            if(f[i][x]!=f[i][y]){
                  x=f[i][x];
                  y=f[i][y];
            }
      }
      return f[0][x];
}

void build_vtree(ll x){
      if(tt==1){
            st[++tt]=x;
            return ;
      }
      ll lca=LYCA(st[tt],x);
      while(tt>1&&dfn[st[tt-1]]>=dfn[lca]){
            edd(st[tt-1],st[tt]);
            tt--;
      }
      if(st[tt]!=lca){
            in[lca]=1;
            edd(lca,st[tt]);
            st[tt]=lca;
      }
      st[++tt]=x;
}

void dp1(ll x,ll o){
      ff[x][0]=ff[x][1]=1;
      in[x]=1;
      for(ll i=head[x];i;i=q[i].nxt){
            ll v=q[i].to;
            if(v==f[0][x]||v==o||in[v]||en[i])continue;
            dp1(v,o);
            ff[x][0]=ff[x][0]*(ff[v][0]+ff[v][1])%mod;
            ff[x][1]=ff[x][1]*ff[v][0]%mod;
      }
}

void getk(ll x,ll y){
      k0[x][0]=1,k1[x][1]=1;
      for(ll i=x;f[0][i]!=y;i=f[0][i]){
            dp1(f[0][i],i);
            ll t0=k0[x][0],t1=k1[x][0];
            k0[x][0]=ff[f[0][i]][0]*(k0[x][0]+k0[x][1])%mod;
            k1[x][0]=ff[f[0][i]][0]*(k1[x][0]+k1[x][1])%mod;
            k0[x][1]=ff[f[0][i]][1]*t0%mod;
            k1[x][1]=ff[f[0][i]][1]*t1%mod;
      }
}

void yu_work(ll x){
      for(ll i=hh[x];i;i=e[i].nx){
            ll v=e[i].v;
            yu_work(v),getk(v,x);
      }
      ff[x][0]=ff[x][1]=1;
      for(ll i=head[x];i;i=q[i].nxt){
            ll v=q[i].to;
            if(in[v]||en[i]||v==f[0][x])continue;
            dp1(v,0);
            ff[x][0]=ff[x][0]*(ff[v][0]+ff[v][1])%mod;
            ff[x][1]=ff[x][1]*ff[v][0]%mod;
      }
}

void DP2(ll x){
      g[x][0]=ff[x][0],g[x][1]=ff[x][1];
      for(ll i=hh[x];i;i=e[i].nx){
            ll v=e[i].v;
            if(v!=f[0][x]){
                  DP2(v);
                  ll f0=(k0[v][0]*g[v][0]%mod+k1[v][0]*g[v][1]%mod)%mod;
                  ll f1=(k0[v][1]*g[v][0]%mod+k1[v][1]*g[v][1]%mod)%mod;
                  g[x][0]=g[x][0]*(f0+f1)%mod;
                  g[x][1]=g[x][1]*f0%mod;
            }
      }
      if(bj[x]==1)g[x][0]=0;
      if(bj[x]==-1)g[x][1]=0;
}

int main(){
      scanf("%lld%lld",&n,&m);
      for(ll i=1;i<=m;i++){
            ll a1,a2;
            scanf("%lld%lld",&a1,&a2);
            add(a1,a2);
      }
      dfs(1,0);
      for(ll i=1;i<=T;i++){
            if(!in[tr[i].u1])in[tr[i].u1]=1,p[++tp]=tr[i].u1;
            if(!in[tr[i].v1])in[tr[i].v1]=1,p[++tp]=tr[i].v1;
      }
      sort(p+1,p+1+tp,cmp);
      st[tt=1]=1;
      for(ll i=1;i<=tp;i++){
            if(p[i]==1)continue;
            build_vtree(p[i]);
      }
      while(tt>1){
            edd(st[tt-1],st[tt]);
            tt--;
      }
      yu_work(1);
      b[1]=1;
      for(ll i=2;i<=tp+1;i++)b[i]=b[i-1]*2;
      for(ll i=0;i<b[tp+1];i++){
            for(ll j=1;j<=tp;j++){
                  if(i&b[j])bj[p[j]]=1;
                  else bj[p[j]]=-1;
            }
            bool pool=0;
            for(ll j=1;j<=T;j++){
                if(bj[tr[j].u1]==1&&bj[tr[j].v1]==1){
                      pool=1;break;
                }
            }
          if(pool)continue;
          DP2(1);
          ans=(ans+(g[1][0]+g[1][1])%mod)%mod;
    }
    printf("%lld",ans);
}

猜你喜欢

转载自www.cnblogs.com/caijiLYC/p/13405780.html
今日推荐