题意:给一张无向点带有权无向图。定义连通图的权值为图中各点权的乘积,图的权值为其包含的各连通图的权和。设z_i为删除i点后图的权值,求$S = (\sum\limits_{i=1}^{n}i\cdot z_i) \text{ mod } (10^9 + 7)$。

显然和点双有关。回忆各种tarjan:缩SCC得DAG,缩边BCC得一棵树,我们要想办法把点BCC也缩成一棵树。

tarjan求点双,然后给每个点双新建一个点,将这个BCC内的所有点连向这个点。

因为点与点之间没有边,SCC与SCC之间没有边,所以可以证明这是一棵树。这个算法有个名字叫Block Forest Data Structure.

缩成树之后随便选一个SCC点作为根,显然所有非叶子点都是割点(SCC虚拟点除外),叶子则都是非割点。

在这棵树上直接DP即可,为了方便计算直接将SCC点权赋为1。

接着是一些注意点:

  1. 孤立点要特殊处理,因为一个点不是点双。

  2. 缩点后的点的个数可能达到$2n$。

  3. 当前点为割点的判定:low[k]>=dfn[x]而不是low[x]==dfn[x],且这里要保证在此之前k未被访问过,具体看代码。

  4. tarjan弹栈的时候要注意,弹到k为止,x特殊处理。因为x和k在栈中可能不是连续的。

UPD:才知道这个就是圆方树。

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. #define mem(a) memset(a,0,sizeof(a))
  5. #define rep(i,l,r) for (int i=(l),_=(r); i<=_; i++)
  6. #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
  7. using namespace std;
  8.  
  9. const int N=,mod=;
  10. int T,n,m,tot,top,u,v,S,tim,bcc,d[N];
  11. int w[N],p[N],val[N],ans[N],stk[N],low[N],dfn[N],bel[N];
  12. bool vis[N];
  13. inline void up(int &x,int y){ x+=y; if (x>=mod) x-=mod; }
  14.  
  15. int ksm(int a,int b){
  16. int res=;
  17. for (; b; a=1ll*a*a%mod,b>>=)
  18. if (b&) res=1ll*res*a%mod;
  19. return res;
  20. }
  21.  
  22. struct E{
  23. int cnt,h[N],to[N<<],nxt[N<<];
  24. void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
  25.  
  26. void dfs(int x,int fa){
  27. vis[x]=; val[x]=w[x]; bel[x]=tot;
  28. For(i,x) if ((k=to[i])!=fa)
  29. dfs(k,x),val[x]=1ll*val[x]*val[k]%mod;
  30. }
  31.  
  32. void DP(int x,int fa){
  33. vis[x]=; ans[x]=(S-p[bel[x]]+mod)%mod;
  34. For(i,x) if ((k=to[i])!=fa) DP(k,x),up(ans[x],val[k]);
  35. up(ans[x],1ll*p[bel[x]]*ksm(val[x],mod-)%mod);
  36. }
  37. }G,G1;
  38.  
  39. void tarjan(int x,int fa){
  40. dfn[x]=low[x]=++tim; stk[++top]=x;
  41. for (int i=G.h[x],k; i; i=G.nxt[i]) if ((k=G.to[i])!=fa){
  42. if (dfn[k]) low[x]=min(low[x],dfn[k]);
  43. else{
  44. tarjan(k,x),low[x]=min(low[x],low[k]);
  45. if (low[k]>=dfn[x]){
  46. bcc++; w[n+bcc]=; int t;
  47. while (){
  48. t=stk[top]; //printf("%d %d\n",t,n+bcc);
  49. G1.add(t,n+bcc); G1.add(n+bcc,t);
  50. top--; if (t==k) break;
  51. }
  52. G1.add(x,n+bcc); G1.add(n+bcc,x);
  53. }
  54. }
  55. }
  56. }
  57.  
  58. void init(){ rep(i,,n+bcc) G.h[i]=G1.h[i]=dfn[i]=d[i]=vis[i]=; G.cnt=G1.cnt=tot=top=tim=bcc=S=; }
  59.  
  60. int main(){
  61. freopen("hdu5739.in","r",stdin);
  62. freopen("hdu5739.out","w",stdout);
  63. for (scanf("%d",&T); T--; init()){
  64. scanf("%d%d",&n,&m);
  65. rep(i,,n) scanf("%d",&w[i]);
  66. rep(i,,m) scanf("%d%d",&u,&v),G.add(u,v),G.add(v,u),d[u]++,d[v]++;
  67. rep(i,,n) if (!dfn[i]) tarjan(i,);
  68. rep(i,n+,n+bcc) if (!vis[i]) tot++,G1.dfs(i,),p[tot]=val[i],up(S,p[tot]);
  69. rep(i,,n) if (!d[i]) up(S,w[i]);
  70. rep(i,,n) if (!d[i]) ans[i]=(S-w[i]+mod)%mod;
  71. //rep(i,1,n+bcc) printf("%d ",val[i]); puts("");
  72. rep(i,,n+bcc) vis[i]=;
  73. rep(i,n+,n+bcc) if (!vis[i]) G1.DP(i,);
  74. //rep(i,1,n) printf("%d ",ans[i]); puts("");
  75. int res=;
  76. rep(i,,n) up(res,1ll*i*ans[i]%mod);
  77. printf("%d\n",res);
  78. }
  79. return ;
  80. }

[HDU5739]Fantasia(圆方树DP)的更多相关文章

  1. [BZOJ2125]最短路(圆方树DP)

    题意:仙人掌图最短路. 算法:圆方树DP,$O(n\log n+Q\log n)$ 首先建出仙人掌圆方树(与点双圆方树的区别在于直接连割边,也就是存在圆圆边),然后考虑点u-v的最短路径,显然就是:在 ...

  2. [BZOJ5463][APIO2018]铁人两项(圆方树DP)

    题意:给出一张图,求满足存在一条从u到v的长度大于3的简单路径的有序点对(u,v)个数. 做了上一题[HDU5739]Fantasia(点双连通分量+DP),这个题就是一个NOIP题了. 一开始考虑了 ...

  3. BZOJ1023:[SHOI2008]cactus仙人掌图(圆方树,DP,单调队列)

    Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌图(cactus). 所谓简单回路就是指在图上不重复经过任何一个顶点 ...

  4. [BZOJ4316]小C的独立集(圆方树DP)

    题意:求仙人掌图直径. 算法:建出仙人掌圆方树,对于圆点直接做普通的树上DP(忽略方点儿子),方点做环上DP并将值直接赋给父亲. 建图时有一个很好的性质,就是一个方点在邻接表里的点的顺序正好就是从环的 ...

  5. 洛谷4630APIO2018铁人两项(圆方树+dp)

    QWQ神仙题啊(据说是今年第一次出现圆方树的地方) 首先根据题目,我们就是求对于每一个路径\((s,t)\)他的贡献就是两个点之间的点数,但是图上问题我并没有办法很好的解决... 这时候考虑圆方树,我 ...

  6. Luogu4630 APIO2018 Duathlon 圆方树、树形DP

    传送门 要求的是一条按顺序经过\(s,t,c\)三个点的简单路径.简单路径的计数问题不难想到点双联通分量,进而使用圆方树进行求解. 首先将原图缩点,对于一个大小为\(size\)的点双联通分量内,在这 ...

  7. [APIO2018] Duathlon 铁人两项 圆方树,DP

    [APIO2018] Duathlon 铁人两项 LG传送门 圆方树+简单DP. 不会圆方树的话可以看看我的另一篇文章. 考虑暴力怎么写,枚举两个点,答案加上两个点之间的点的个数. 看到题面中的一句话 ...

  8. [APIO2018]铁人两项——圆方树+树形DP

    题目链接: [APIO2018]铁人两项 对于点双连通分量有一个性质:在同一个点双里的三个点$a,b,c$,一定存在一条从$a$到$c$的路径经过$b$且经过的点只被经过一次. 那么我们建出原图的圆方 ...

  9. loj2587 「APIO2018」铁人两项[圆方树+树形DP]

    主要卡在一个结论上..关于点双有一个常用结论,也经常作为在圆方树/简单路径上的良好性质,对于任意点双内互不相同的三点$s,c,t$,都存在简单路径$s\to c\to t$,证明不会.可以参见clz博 ...

随机推荐

  1. [codechef FNCS]分块处理+树状数组

    题目链接:https://vjudge.net/problem/CodeChef-FNCS 在一个地方卡了一晚上,就是我本来以为用根号n分组,就会分成根号n个.事实上并不是....因为用的是根号n下取 ...

  2. HDU 多校对抗赛 B Balanced Sequence

    Balanced Sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  3. 【Foreign】远行 [LCT]

    远行 Time Limit: 20 Sec  Memory Limit: 256 MB Description Input Output Sample Input 0 5 10 1 4 5 2 3 2 ...

  4. 【洛谷 P1363】幻想迷宫(搜索)

    这题其实可以很简单. 题目叫做"幻想迷宫",那么我们就幻想一个迷宫. 借用一下@FancyDreams的图片 只有左上角第一个\(5*4\)的迷宫是真的, 其他都是我们幻想出来的. ...

  5. HTML5之FileReader的简易使用

    用来把文件读入内存,并且读取文件中的数据.FileReader接口提供了一个异步API,使用该API可以在浏览器主线程中异步访问文件系统,读取文件中的数据.FileReader接口提供了读取文件的方法 ...

  6. 前端开发各种cross之cross domain

    作为一个苦逼前端开发工程师,不得不面对各种cross,比如面对五花八门的浏览器我们必须cross browser,面对各种终端,我们必须cross device,在这么多年的前端开发经历中,在不同的域 ...

  7. MySQL中EXISTS的用法

    比如在Northwind数据库中有一个查询为 SELECT c.CustomerId,CompanyName FROM Customers c WHERE EXISTS( SELECT OrderID ...

  8. Linux的yum命令——(八)

    Yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器.基于RPM包管理,能够从指定的服务器自动下载 ...

  9. IPsec传输模式下ESP报文的装包和拆包过程

    原创文章,拒绝转载 装包过程 总体流程图 过程描述 在原IP报文中找到TCP报文部分,在其后添加相应的ESP trailer信息. ESP trailer 包含三部分:Padding,Pad leng ...

  10. HDU1281(二分图最大匹配,棋盘建图,找关键点)

    棋盘游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...