题意:给一张无向点带有权无向图。定义连通图的权值为图中各点权的乘积,图的权值为其包含的各连通图的权和。设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:才知道这个就是圆方树。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define mem(a) memset(a,0,sizeof(a))
#define rep(i,l,r) for (int i=(l),_=(r); i<=_; i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
using namespace std; const int N=,mod=;
int T,n,m,tot,top,u,v,S,tim,bcc,d[N];
int w[N],p[N],val[N],ans[N],stk[N],low[N],dfn[N],bel[N];
bool vis[N];
inline void up(int &x,int y){ x+=y; if (x>=mod) x-=mod; } int ksm(int a,int b){
int res=;
for (; b; a=1ll*a*a%mod,b>>=)
if (b&) res=1ll*res*a%mod;
return res;
} struct E{
int cnt,h[N],to[N<<],nxt[N<<];
void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } void dfs(int x,int fa){
vis[x]=; val[x]=w[x]; bel[x]=tot;
For(i,x) if ((k=to[i])!=fa)
dfs(k,x),val[x]=1ll*val[x]*val[k]%mod;
} void DP(int x,int fa){
vis[x]=; ans[x]=(S-p[bel[x]]+mod)%mod;
For(i,x) if ((k=to[i])!=fa) DP(k,x),up(ans[x],val[k]);
up(ans[x],1ll*p[bel[x]]*ksm(val[x],mod-)%mod);
}
}G,G1; void tarjan(int x,int fa){
dfn[x]=low[x]=++tim; stk[++top]=x;
for (int i=G.h[x],k; i; i=G.nxt[i]) if ((k=G.to[i])!=fa){
if (dfn[k]) low[x]=min(low[x],dfn[k]);
else{
tarjan(k,x),low[x]=min(low[x],low[k]);
if (low[k]>=dfn[x]){
bcc++; w[n+bcc]=; int t;
while (){
t=stk[top]; //printf("%d %d\n",t,n+bcc);
G1.add(t,n+bcc); G1.add(n+bcc,t);
top--; if (t==k) break;
}
G1.add(x,n+bcc); G1.add(n+bcc,x);
}
}
}
} 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=; } int main(){
freopen("hdu5739.in","r",stdin);
freopen("hdu5739.out","w",stdout);
for (scanf("%d",&T); T--; init()){
scanf("%d%d",&n,&m);
rep(i,,n) scanf("%d",&w[i]);
rep(i,,m) scanf("%d%d",&u,&v),G.add(u,v),G.add(v,u),d[u]++,d[v]++;
rep(i,,n) if (!dfn[i]) tarjan(i,);
rep(i,n+,n+bcc) if (!vis[i]) tot++,G1.dfs(i,),p[tot]=val[i],up(S,p[tot]);
rep(i,,n) if (!d[i]) up(S,w[i]);
rep(i,,n) if (!d[i]) ans[i]=(S-w[i]+mod)%mod;
//rep(i,1,n+bcc) printf("%d ",val[i]); puts("");
rep(i,,n+bcc) vis[i]=;
rep(i,n+,n+bcc) if (!vis[i]) G1.DP(i,);
//rep(i,1,n) printf("%d ",ans[i]); puts("");
int res=;
rep(i,,n) up(res,1ll*i*ans[i]%mod);
printf("%d\n",res);
}
return ;
}

[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. Swing学习篇 API [一]Swing常用组件

    1.按钮(Jbutton) Swing中的按钮是Jbutton,它是javax.swing.AbstracButton类的子类,swing中的按钮可以显示图像,并且可以将按钮设置为窗口的默认图标,而且 ...

  2. angularjs的验证信息的写法

    <div ng-messages="alarmDelayForm.alarmRuleName.$error" role="alert"> <d ...

  3. Kafka自我学习3-Scalable

    1.After created the zookeeper cluster, we found all broker cluster topic can be find in zoo1, zoo2, ...

  4. 我自己的python开发环境

    1.开发工具 eclipse 所有的版本下载: https://www.eclipse.org/downloads/index-packages.php , 我下载的是比较低的版本:https://w ...

  5. js如何弹出新窗口

    js如何弹出新窗口 时间:2012-4-22 弹出新窗口也是在网页设计中会经常用到的,其用法也很简单,是通过调用javascript的内置函数windows.open来产生的.  window.ope ...

  6. php中的split函数

    字符串分割函数:split函数 <?php $email='microsoft@exam!ple.com'; $domain = split('\.|@|!',$email);//split分割 ...

  7. 51nod 拉勾专业算法能力测评消灭兔子 优先队列+贪心

    题目传送门 这道题一开始想了很久...还想着写网络流 发现根本不可能.... 然后就想着线段树维护然后二分什么的 最后发现优先队列就可以了 代码还是很简洁的啦 233 就是把兔子按血量从大到小排序一下 ...

  8. Sequence(ST表)(洛谷P2048)

    超级钢琴 知识储备 在做这道题前,我们先要了解一下ST表(一种离线求区间最值的方法) ST表使用DP实现的,其查询复杂度为O(1). 那么我们怎么用DP实现呢?? 首先,我们设立一个状态f[i][j] ...

  9. mongoDB支持的数据类型

    下表为MongoDB中常用的几种数据类型. 数据类型 描述 String 字符串.存储数据常用的数据类型.在 MongoDB 中,UTF-8 编码的字符串才是合法的. Integer 整型数值.用于存 ...

  10. MySQL 查询语句练习2

    创建表 /* Navicat MySQL Data Transfer Source Server : localhost_3306 Source Server Version : 50719 Sour ...