HDU5739 Fantasia 树形dp + 点双缩点

这个题当时打多校的时候有思路,但是代码能力差,没有写出来

事后看zimpha巨巨的题解,看了觉得基本差不多

核心思路:就是找出割点,然后变成森林,然后树形dp就可以搞了

              关键就在重新构图上,缩完点以后,一个割点至少在两个点双里面,这个时候

              把割点拿出来,分别和点双连边,也就是说,缩完的点双是不包含割点的,这个可以人为搞一下

              (像有的点双里面只包含一个桥边,如果把割点拿出来,点双里面没有点了,这个时候把点双的权值积设为1就好)

              然后说是树形dp,其实就是逆元搞一搞,这个很简单,树形dp只处理割点的答案

注意:这个图不联通,这是一个点,还有孤立点,我一直wa在孤立点上,(我的点双模板只能解决至少包含两个点的连通图,这个题丰满了我的模板,感谢)

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <vector>
#include <math.h>
#include <stack>
#include <map>
using namespace std;
typedef long long LL;
const int N = 1e5+5;
const LL mod = 1e9+7;
LL qpow(LL a,LL b){
  LL ret=1;
  while(b){
    if(b&1)ret=(ret*a)%mod;
    b>>=1;
    a=(a*a)%mod;
  }
  return ret;
}
int head[N<<1],tot,T,n,m;
struct Edge{
  int u,v,next;
}edge[N*4];
void addedge(int u,int v){
  edge[tot].u=u;
  edge[tot].v=v;
  edge[tot].next=head[u];
  head[u]=tot++;
}
int low[N],dfn[N],clk,cnt,belbcc[N];
bool iscut[N];
vector<int>bcc[N],cut;
stack<int>s;
void init(){
  memset(dfn,0,sizeof(dfn));
  memset(head,-1,sizeof(head));
  memset(belbcc,0,sizeof(belbcc));
  tot=clk=cnt=0;
  cut.clear();
}
LL w[N],bccw[N<<1],ret[N],blkw[N<<1],zong;
bool vis[N<<1];
int blk,bel[N<<1];
void dfs(int u,int f){
  dfn[u]=low[u]=++clk;
  int child=0;
  if(head[u]==-1){
    ++cnt;bcc[cnt].clear();
    bcc[cnt].push_back(u);
    belbcc[u]=cnt;
    return ;
  }
  for(int i=head[u];~i;i=edge[i].next){
     int to=edge[i].v;
     if(!dfn[to]){
        ++child;s.push(i);dfs(to,u);
        low[u]=min(low[u],low[to]);
        if(low[to]>=dfn[u]){
          iscut[u]=true;++cnt;int x;bcc[cnt].clear();
          do{
             x=s.top();s.pop();

             if(belbcc[edge[x].u]!=cnt){
              bcc[cnt].push_back(edge[x].u);
              belbcc[edge[x].u]=cnt;
             }

             if(belbcc[edge[x].v]!=cnt){
              bcc[cnt].push_back(edge[x].v);
              belbcc[edge[x].v]=cnt;
             }
          }while(x!=i);
        } 
     }
     else if(dfn[to]<dfn[u]&&to!=f){
          s.push(i);
          low[u]=min(low[u],dfn[to]);
     }
  }
  if(!f&&child==1)iscut[u]=false;
  if(iscut[u])cut.push_back(n+u),bccw[n+u]=w[u];
}
void predfs(int u,int f){
    bel[u]=blk;blkw[blk]=blkw[blk]*bccw[u]%mod;
    vis[u]=true;
    for(int i=head[u];~i;i=edge[i].next){
        int v=edge[i].v;
        if(v==f)continue;
        predfs(v,u);
    }
}
LL treedp(int u,int f){
   LL pro=1,sum=0,tmp;
   vis[u]=false;
   for(int i=head[u];~i;i=edge[i].next){
      int v=edge[i].v;
      if(v==f)continue;
      tmp=treedp(v,u);
      pro=pro*tmp%mod;
      sum=(sum+tmp)%mod;
   }
   pro=pro*bccw[u]%mod;
   if(u>n){
     tmp=blkw[bel[u]];
     tmp=tmp*qpow(pro,mod-2)%mod;
     sum=(sum+tmp)%mod;
     tmp=zong-blkw[bel[u]];
     while(tmp<0)tmp+=mod; 
     ret[u-n]=(sum+tmp)%mod;
   }
   return pro; 
}
int main(){  
  scanf("%d",&T);
  while(T--){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%I64d",&w[i]);
    init();
    for(int i=0;i<m;++i){
      int u,v;scanf("%d%d",&u,&v);
      addedge(u,v);addedge(v,u);
    }
    for(int i=1;i<=n;++i)if(!dfn[i])dfs(i,0);
    tot=0;memset(head,-1,sizeof(head));
    for(int i=1;i<=cnt;++i){
       bccw[i]=1;
       for(int j=0;j<bcc[i].size();++j){
          int u=bcc[i][j];
          if(iscut[u]) addedge(i,u+n),addedge(u+n,i);
          else  bccw[i]=bccw[i]*w[u]%mod;
       }
    }
    blk=0;
    for(int i=1;i<=cnt;++i)
     if(!vis[i]){++blk;blkw[blk]=1;predfs(i,0);}
    for(int i=0;i<cut.size();++i)
     if(!vis[cut[i]]){++blk;blkw[blk]=1;predfs(cut[i],0);}
    zong=0;
    for(int i=1;i<=blk;++i)zong=(zong+blkw[i])%mod;
    for(int i=1;i<=cnt;++i)
     if(vis[i])treedp(i,0);
    for(int i=0;i<cut.size();++i)
     if(vis[cut[i]])treedp(cut[i],0);
    LL ans=0;
    for(int i=1;i<=n;++i){
        if(iscut[i]){
           ans=(ans+1ll*i*ret[i]%mod)%mod;
           iscut[i]=false;
        }
        else{
           int k=belbcc[i];
           k=bel[k];
           LL ad=0;
           if(w[i]!=blkw[k])ad=blkw[k]*qpow(w[i],mod-2)%mod;
           LL tmp=zong-blkw[k];
           while(tmp<0)tmp+=mod;
           tmp=(tmp+ad)%mod;
           ans=(ans+1ll*i*tmp%mod)%mod;
        }
    }
    printf("%I64d
",ans);
  }
  return 0;
}
View Code
原文地址:https://www.cnblogs.com/shuguangzw/p/5694952.html