题意:给一张无向点带有权无向图。定义连通图的权值为图中各点权的乘积,图的权值为其包含的各连通图的权和。设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=3000010,mod=1000000007;
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=1;
17     for (; b; a=1ll*a*a%mod,b>>=1)
18         if (b&1) res=1ll*res*a%mod;
19     return res;
20 }
21
22 struct E{
23     int cnt,h[N],to[N<<1],nxt[N<<1];
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]=1; 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]=1; 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-2)%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]=1; int t;
47                 while (1){
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,0,n+bcc) G.h[i]=G1.h[i]=dfn[i]=d[i]=vis[i]=0; G.cnt=G1.cnt=tot=top=tim=bcc=S=0; }
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,1,n) scanf("%d",&w[i]);
66         rep(i,1,m) scanf("%d%d",&u,&v),G.add(u,v),G.add(v,u),d[u]++,d[v]++;
67         rep(i,1,n) if (!dfn[i]) tarjan(i,0);
68         rep(i,n+1,n+bcc) if (!vis[i]) tot++,G1.dfs(i,0),p[tot]=val[i],up(S,p[tot]);
69         rep(i,1,n) if (!d[i]) up(S,w[i]);
70         rep(i,1,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,0,n+bcc) vis[i]=0;
73         rep(i,n+1,n+bcc) if (!vis[i]) G1.DP(i,0);
74         //rep(i,1,n) printf("%d ",ans[i]); puts("");
75         int res=0;
76         rep(i,1,n) up(res,1ll*i*ans[i]%mod);
77         printf("%d\n",res);
78     }
79     return 0;
80 }

转载于:https://www.cnblogs.com/HocRiser/p/9110226.html

[HDU5739]Fantasia(圆方树DP)相关推荐

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

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

  2. 【知识小结】圆方树 广义圆方树

    关于仙人掌的总结 immortalCO的博客 yyb的博客 模板 namespace T{vector <int> e[maxn * 2];int tag[maxn * 2];void a ...

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

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

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

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

  5. [XSY] 绿色(圆方树、树形DP、树上差分)

    绿色 题意简述 题解 首先,每次修改完点权后,重新考虑一遍所有路径显然是不现实的,所以我们考虑求出经过每个点的两端同色的简单路径数,这样权值和容易统计和修改. 接下来分析仙人掌上的简单路径性质.一条简 ...

  6. 仙人掌相关问题的解法(1)-DFS树解决仙人掌DP问题,圆方树

    前言 有难度的仙人掌题在近几年也只是在国家集训队水平的比赛里才会出现. 不过,这不是说仙人掌对国集水平以下的选手意义不大: 首先,仙人掌暴力 DP 问题难度并不大,在省选. NOI 甚至 NOIP 中 ...

  7. cactus仙人掌图【仙人掌圆方树+树形DP+单调队列】

    题目链接 BZOJ 1023 首先,圆方树是比较好想到的,维护直径,我们最方便的做法就是先让它变成一棵树,这里因为是仙人掌图,所以就用圆方树来构建. 再者,就是维护直径了,比较好想到的是非环上结点,就 ...

  8. Fantasia 【HDU - 5739】【广义圆方树】

    题目链接 这道题的题意在这里就不展开了,因为这次的英文比较的好读,也没有生词,好懂一些些hh. 然后,这道题的关键点,肯定就是来看这个点是不是一个割点,也就是割去这个点之后就将原来的联通块展开成两个以 ...

  9. [学习笔记]圆方树广义圆方树

    引入 偶尔,我们会遇到一些要在无向图/仙人掌上做的问题,这些问题如果在树上就会比较方便,那么我们就开始考虑能不能把原图等效成一棵树,然后就可以方便地乱搞了? 圆方树就是一种将无向图/仙人掌变成树的数据 ...

  10. 仙人掌圆方树学习笔记

    终于对仙人掌有了一点初步的理解. 仙人掌 仙人掌是什么? 仙人掌是一个无向图. 仙人掌有什么特点? 仙人掌的每条边只属于一个简单环. 下面是一个栗子 有什么用呢? 我们可以先用\(tarjan\)找出 ...

最新文章

  1. 多项式加法 java 链表_多项式加法,用单链表实现。
  2. keil编译出错关于__use_no_semihosting_swi的使用
  3. 操作系统习题3—进程的互斥与同步
  4. uefi启动 多硬盘gtp_传统引导更改为UEFI+GTP
  5. 谷粒商城RabbitMQ锁库存逻辑详解--新理解(长文警告)
  6. C# 5.0 CallerMemberName CallerFilePath CallerLineNumber 在.NET4中的使用
  7. vue res返回html,vue 组件内获取actions的response方式
  8. 2、计算浮点数相除的余数
  9. 电池pack结构_锂电池pack性能测试标准,电池测试模组就选弹片微针模组
  10. python如何读取二进制文件为图片_关于Python获取图片文件二进制数据的问题(获取为空)...
  11. Python 爬虫案例
  12. 个性和共性,对共性的封装。新的语言是如何诞生的
  13. 优优自走棋2.1.1
  14. ae制h5文字动画_html5酷炫的文字打字动画特效
  15. ScannerException: while scanning for the next token found character ‘@‘ 问题解决
  16. 2017-2018-1 Java演绎法 小组会议及交互汇总
  17. 初识MFC----MFC简介
  18. 从老板的裤裆拉链看 Google 管理之道
  19. 计算机网络实验Lab1 Test
  20. 第十四次CCF CSP认证心得

热门文章

  1. 【5分钟 Paper】Playing Atari with Deep Reinforcement Learning
  2. 协同过滤之ALS-WR算法
  3. 基于矩阵分解的隐因子模型
  4. 先安装windows7_64bit,再安装Centos7,windows7的启动项不出现
  5. matconvnet在MATLAB2013配置过程中遇到的问题
  6. 【转】Canny 算法
  7. 20170114 - Mac 向上一级文件夹快捷键
  8. glog学习(二):glog主要接口和类分析
  9. 001---需求分析
  10. 201571030139/201571030134 小学生四则运算软件结对编程