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\n",ans);
  }
  return 0;
}

时间: 2024-11-05 20:36:53

HDU5739 Fantasia 树形dp + 点双缩点的相关文章

hdoj 2242 考研路茫茫——空调教室 【无向图求边双联通 缩点 + 树形dp】

考研路茫茫--空调教室 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2447    Accepted Submission(s): 721 Problem Description 众所周知,HDU的考研教室是没有空调的,于是就苦了不少不去图书馆的考研仔们.Lele也是其中一个.而某教室旁边又摆着两个未装上的空调,更是引起人们无限YY

HDU 2242 考研路茫茫——空调教室 (双连通分量+树形DP)

题目地址:HDU 2242 先用双连通分量缩点,然后形成一棵树,然后在树上做树形DP,求出每个点的子树和.然后找最小值即可.需要注意一下重边的问题,第一次返回父节点时可以忽略,因为这是反向边,然后之后再返回的时候就不是反向边了.不能忽略了. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #i

考研路茫茫 (双连通 树形dp)

这道题就是模板的题加上一道很水的树形dp 感觉就先用 1,双连通缩点,如果只存在一个双连通分量,那么肯定是删除任何一个点,这个图还是连通的, 2,利用树形dp把缩点后连成一个图,然后用树形dp的一个dfs就算出答案了 #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <stack&g

HDU 2242 连通分量缩点+树形dp

题目大意是: 所有点在一个连通图上,希望去掉一条边得到两个连通图,且两个图上所有点的权值的差最小,如果没有割边,则输出impossible 这道题需要先利用tarjan算法将在同一连通分量中的点缩成一个点后,重新构建一幅图,然后利用新建的图进行树形dp解决问题 这道题目需要注意的是可能存在重边,那么子节点回到父节点有两条边及以上的话,就需要对子节点经过父节点的边进行low值更新 tarjan算法学习的网站个人感觉还不错https://www.byvoid.com/blog/scc-tarjan/

HDU5739 Fantasia(点双连通分量 + Block Forest Data Structure)

题目大概说给一张无向点带有权无向图.定义连通图的权值为图中各点权的乘积,图的权值为其包含的各连通图的权和.设$z_i$为删除i点后图的权值,求$S = (\sum\limits_{i=1}^{n}i\cdot z_i) \text{ mod } (10^9 + 7)$. 官方题解这么说的: 显然, 只要删掉关键点才会使图不联通. 对于其他点, 权值很容易计算. 首先求出所有的点双联通分量, 对于每一个点双联通分量$S$, 新建一个节点$s$, 向$S$中每个节点$v$连边. 这样一来, 新增的点

[Bzoj 2427] [HAOI2010] 软件安装 tarjan缩点+树形DP

题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一 些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大). 但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j).幸运的 是,一个软件最多依赖另外一个软件.如果一个软件不能正常工作,那么它能够发挥的作用为0. 我们现在知道了软件之间的依赖关系:软件i依赖软件Di.现在请你设计出

hdu2242(树形dp+tarjan+缩点)

hdu2242 http://acm.hdu.edu.cn/showproblem.php?pid=2242 给定n,m表示n个点,m条边 每个点有个权值 问我们删除两某条边(割边)后将图分为两个部分,要使得两个部分的权值之差最小 这题的弱化版本是在一棵树上删除某条边后后将图分为两个部分,要使得两个部分的权值之差最小.是用树形dp来做的 但是这道题目是个图,但是我们可以转化为树,即将图中的边连通分量求出来,然后缩成一个点,建出一个新的树图,那么就可以用树形dp来求解题目了. 1 #include

【BZOJ-3572】世界树 虚树 + 树形DP

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种

BZOJ 1093 最大半连通子图(强连通分量+树形DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1093 题意: 思路:(1)首先,强连通分量中的一个点若在最大半连通子图中,则必定整个连通分量中的点都在,因为都在还是满足半连通的性质而且使得节点数更多. (2)因此,求出强连通分量缩点,形成一个有向无环图,其实与树是差不多的.在这个图上DP一次即可,也就是找出最长链以及最长链的个数. vector<int> g[N],g1[N]; int n,m,mod; int dfn[N],lo