题意:给一张无向点带有权无向图。定义连通图的权值为图中各点权的乘积,图的权值为其包含的各连通图的权和。设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. bzoj 3720 Gty的妹子树 树分块?瞎搞

    Gty的妹子树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2149  Solved: 781[Submit][Status][Discuss] D ...

  2. php魔术方法的使用

    本文测试环境为 php5.5.12 一.__get .__set 将对象的属性进行接管. 一般来说,总是把类的属性定义为private,但是对属性的读取和赋值操作非常频繁,在php5+,预定义__se ...

  3. C++ Review

    #include "iostream" #include "iomanip" #include "cstdio" using namespa ...

  4. BZOJ 4527: K-D-Sequence

    4527: K-D-Sequence Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 163  Solved: 66[Submit][Status][D ...

  5. 【BZOJ】[SDOI2009]HH的项链

    [算法]主席树||离线+树状数组 [题解] 主席树经典应用:找区间不同的数字个数. 做法:记录每个数上一次出现的位置last[i],对last建权值线段树,对于区间询问last[i]<L的数字个 ...

  6. system32下 exe文件作用

    system32下EXE文件的作用说明 A accwiz.exe 辅助功能向导 ahui.exe 应用程序兼容用户界面 alg.exe 为 Internet 连接共享和 Internet 连接防火墙提 ...

  7. python升级3.6后 yum出错File "/usr/bin/yum", line 30 ^

    问题描述: # yum provides ifconfig File "/usr/bin/yum", line 30 except KeyboardInterrupt, e: ^ ...

  8. 用python写爬虫笔记(一)

    https://bitbucket.org/wswp/code   http://example.webscraping.com http://www.w3schools.com selenium.g ...

  9. linux shell 脚本实现tcp/upd协议通讯(重定向应用)

    linux shell 脚本实现tcp/upd协议通讯(重定向应用) http://www.cnblogs.com/chengmo/archive/2010/10/22/1858302.html

  10. swap增加

    #创建17G的文件dd if=/dev/zero of=/data/swap/swap-1 bs=1024 count=16255000#设置为swap分区/sbin/mkswap /data/swa ...