[HDU5739]Fantasia(圆方树DP)
题意:给一张无向点带有权无向图。定义连通图的权值为图中各点权的乘积,图的权值为其包含的各连通图的权和。设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)的更多相关文章
- [BZOJ2125]最短路(圆方树DP)
题意:仙人掌图最短路. 算法:圆方树DP,$O(n\log n+Q\log n)$ 首先建出仙人掌圆方树(与点双圆方树的区别在于直接连割边,也就是存在圆圆边),然后考虑点u-v的最短路径,显然就是:在 ...
- [BZOJ5463][APIO2018]铁人两项(圆方树DP)
题意:给出一张图,求满足存在一条从u到v的长度大于3的简单路径的有序点对(u,v)个数. 做了上一题[HDU5739]Fantasia(点双连通分量+DP),这个题就是一个NOIP题了. 一开始考虑了 ...
- BZOJ1023:[SHOI2008]cactus仙人掌图(圆方树,DP,单调队列)
Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌图(cactus). 所谓简单回路就是指在图上不重复经过任何一个顶点 ...
- [BZOJ4316]小C的独立集(圆方树DP)
题意:求仙人掌图直径. 算法:建出仙人掌圆方树,对于圆点直接做普通的树上DP(忽略方点儿子),方点做环上DP并将值直接赋给父亲. 建图时有一个很好的性质,就是一个方点在邻接表里的点的顺序正好就是从环的 ...
- 洛谷4630APIO2018铁人两项(圆方树+dp)
QWQ神仙题啊(据说是今年第一次出现圆方树的地方) 首先根据题目,我们就是求对于每一个路径\((s,t)\)他的贡献就是两个点之间的点数,但是图上问题我并没有办法很好的解决... 这时候考虑圆方树,我 ...
- Luogu4630 APIO2018 Duathlon 圆方树、树形DP
传送门 要求的是一条按顺序经过\(s,t,c\)三个点的简单路径.简单路径的计数问题不难想到点双联通分量,进而使用圆方树进行求解. 首先将原图缩点,对于一个大小为\(size\)的点双联通分量内,在这 ...
- [APIO2018] Duathlon 铁人两项 圆方树,DP
[APIO2018] Duathlon 铁人两项 LG传送门 圆方树+简单DP. 不会圆方树的话可以看看我的另一篇文章. 考虑暴力怎么写,枚举两个点,答案加上两个点之间的点的个数. 看到题面中的一句话 ...
- [APIO2018]铁人两项——圆方树+树形DP
题目链接: [APIO2018]铁人两项 对于点双连通分量有一个性质:在同一个点双里的三个点$a,b,c$,一定存在一条从$a$到$c$的路径经过$b$且经过的点只被经过一次. 那么我们建出原图的圆方 ...
- loj2587 「APIO2018」铁人两项[圆方树+树形DP]
主要卡在一个结论上..关于点双有一个常用结论,也经常作为在圆方树/简单路径上的良好性质,对于任意点双内互不相同的三点$s,c,t$,都存在简单路径$s\to c\to t$,证明不会.可以参见clz博 ...
随机推荐
- [codechef FNCS]分块处理+树状数组
题目链接:https://vjudge.net/problem/CodeChef-FNCS 在一个地方卡了一晚上,就是我本来以为用根号n分组,就会分成根号n个.事实上并不是....因为用的是根号n下取 ...
- HDU 多校对抗赛 B Balanced Sequence
Balanced Sequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- 【Foreign】远行 [LCT]
远行 Time Limit: 20 Sec Memory Limit: 256 MB Description Input Output Sample Input 0 5 10 1 4 5 2 3 2 ...
- 【洛谷 P1363】幻想迷宫(搜索)
这题其实可以很简单. 题目叫做"幻想迷宫",那么我们就幻想一个迷宫. 借用一下@FancyDreams的图片 只有左上角第一个\(5*4\)的迷宫是真的, 其他都是我们幻想出来的. ...
- HTML5之FileReader的简易使用
用来把文件读入内存,并且读取文件中的数据.FileReader接口提供了一个异步API,使用该API可以在浏览器主线程中异步访问文件系统,读取文件中的数据.FileReader接口提供了读取文件的方法 ...
- 前端开发各种cross之cross domain
作为一个苦逼前端开发工程师,不得不面对各种cross,比如面对五花八门的浏览器我们必须cross browser,面对各种终端,我们必须cross device,在这么多年的前端开发经历中,在不同的域 ...
- MySQL中EXISTS的用法
比如在Northwind数据库中有一个查询为 SELECT c.CustomerId,CompanyName FROM Customers c WHERE EXISTS( SELECT OrderID ...
- Linux的yum命令——(八)
Yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器.基于RPM包管理,能够从指定的服务器自动下载 ...
- IPsec传输模式下ESP报文的装包和拆包过程
原创文章,拒绝转载 装包过程 总体流程图 过程描述 在原IP报文中找到TCP报文部分,在其后添加相应的ESP trailer信息. ESP trailer 包含三部分:Padding,Pad leng ...
- HDU1281(二分图最大匹配,棋盘建图,找关键点)
棋盘游戏 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...