HDU 5739 Fantasia(点双连通分量+树形DP)
来源:互联网 发布:学士后java课程 编辑:程序博客网 时间:2024/04/28 22:00
Description
定义一个连通图的权值为所有顶点点权乘积,定义一个无向图的权值为这个无向图的极大连通子图权值和,现给出一张有n个点的无向图,每个点有点权wi,设删去节点i后此图权值为z[i],求
Input
第一行一整数T表示用例组数,每组用例首先输入两个整数n和m表示点数和边数,之后输入n个整数wi表示每个点的点权,最后m行每行两个整数u个v表示u和v之间有一条边相连
(T<=1000,2<=n<=10^5,1<=m<=2*10^5,1<=wi<=10^9,sigma(n),sigma(m)<=1.5*10^6
Output
对于每组用例,输出一个整数S
Sample Input
1
3 2
1 2 3
1 2
2 3
Sample Output
20
Solution
先对原图用tarjan求一遍点双连通分量,对每个块用一个vector记录其中的点,对每个块新加一个点,重新建图,让这个点连向块内所有边,这样就可以得到一个至多block棵树的森林(block为块数),而且这个森林的连通性与原图连通性一致,从编号大的顶点为根节点对这张图dfs,令mul[i]表示以i节点为根的子树权值(如果i不是新加的点,那么这个子树必然是原图中一连通图),令sum[i]表示以i节点为根的所有子树权值和,为避免新加点对权值的影响,可以令新加的点的点权为1,在dfs过程中进行树形DP可以求出所有的mul和sum值,稍微分析下这棵树和dfs过程会发现以下几点:
1.孤立点的根节点是自身
2.非孤立点的根节点是新加节点
3.割点是非叶子节点
4.非割点都是叶子节点
令cnt为所有根节点的mul值之和
那么对于非孤立点i,删掉它后原图的权值就是mul[root[i]]/mul[i]+sum[i]+cnt-mul[root[i]]
对于孤立点i,删掉它原图的权值就是cnt-mul[i]
Code
#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<vector>using namespace std;typedef long long ll;#define mod 1000000007ll#define maxn 111111vector<int>g[2*maxn];int low[maxn],dfn[maxn],stack[maxn];int index,top;int block;//块的数量 int bridge;//桥的数量 bool instack[maxn];vector<int>vec[maxn];//vec[i]表示第i个块中的点集,i从1~block void add_edge(int u,int v){ g[u].push_back(v),g[v].push_back(u);}void Tarjan(int u,int pre){ int v; low[u]=dfn[u]=++index; stack[top++]=u; instack[u]=1; for(int i=0;i<g[u].size();i++) { v=g[u][i]; if(v==pre)continue; if(!dfn[v]) { Tarjan(v,u); if(low[u]>low[v])low[u]=low[v]; if(low[v]>dfn[u])bridge++;//找到一个桥 if(low[v]>=dfn[u])//找到一个割点u { block++; vec[block].clear(); int vn; do { vn=stack[--top]; vec[block].push_back(vn); instack[vn]=0; }while(vn!=v); vec[block].push_back(u);//割点可能还属于其他块所以不能出栈 } } else if(instack[v]&&low[u]>dfn[v]) low[u]=dfn[v]; }}void solve(int n){ memset(dfn,0,sizeof(dfn)); memset(instack,0,sizeof(instack)); index=block=top=bridge=0; for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i,-1);}int T,n,m,w[2*maxn],vis[2*maxn],root[2*maxn];ll mul[2*maxn],sum[2*maxn];void dfs(int u){ vis[u]=1,mul[u]=w[u],sum[u]=0; for(int i=0;i<g[u].size();i++) { int v=g[u][i]; if(vis[v])continue; root[v]=root[u]; dfs(v); mul[u]=mul[u]*mul[v]%mod; sum[u]=(sum[u]+mul[v])%mod; }}ll mod_pow(ll a,ll b){ a%=mod; ll ans=1; while(b) { if(b&1)ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans;}int main(){ scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&w[i]),g[i].clear(); for(int i=0;i<m;i++) { int u,v; scanf("%d%d",&u,&v); add_edge(u,v); } solve(n); for(int i=1;i<=n+block;i++)g[i].clear(); for(int k=1;k<=block;k++) { w[n+k]=1; for(int i=0;i<vec[k].size();i++) { int v=vec[k][i]; add_edge(v,n+k); } } memset(vis,0,sizeof(vis)); for(int i=n+block;i;i--) if(!vis[i]) root[i]=i,dfs(i); ll cnt=0,ans=0; for(int i=1;i<=n+block;i++) if(root[i]==i)cnt=(cnt+mul[i])%mod; for(int i=1;i<=n;i++) { ll temp; if(root[i]!=i) { temp=mul[root[i]]*mod_pow(mul[i],mod-2)%mod; temp=((temp+sum[i])%mod+(cnt-mul[root[i]]+mod)%mod)%mod; } else temp=(cnt-mul[i]+mod)%mod; ans=(ans+i*temp%mod)%mod; } printf("%I64d\n",ans); } return 0;}
- HDU 5739 Fantasia(点双连通分量+树形DP)
- Hdu-5739 Fantasia (图论点双连通分量+DP)
- 【双连通分量(tarjan)+树形dp】HDU-2242-考研路茫茫——空调教室
- 【HDU 5739】Fantasia(点双连通+dfs)
- hdu 5739 点双连通分量+乘法逆元超详细讲解
- HDU 4183 Pahom on Water(点双连通分量)
- HDU 3394 Railway(点双连通分量)
- hdu 3394 Railway(Tarjan,点双连通分量)
- HDU 3749 Financial Crisis (点双连通分量)
- HDU 3394 Railway 点双连通分量
- hdu 3749 点双连通分量
- HDU 3394 Railway 点双连通分量
- HDU 3394 Railway 点双连通分量
- 点双连通分量
- 点双连通分量
- 点双连通分量
- 点双连通分量
- HDU 2242 考研路茫茫——空调教室 (双连通分量+树形DP)
- iOS 开发中的争议(一)
- mac开启safari开发模式(用于OC和JS交互)
- 第18周 C语言实战105例 实例2:递增递减操作符
- 有关KVM的安装和使用
- listagg和wmsys.wm_concat, f_concatenate_str---无内容
- HDU 5739 Fantasia(点双连通分量+树形DP)
- android固件集成
- (五)在前端增加相应代码显示文档
- CSS Sprites
- JAVA的Random类(转)
- IOS(swift语言)第3天、类,继承
- 每天为自己充点电还是好的虽然前期的知识有点乏味,但走一步是一步的心态不能变。嗯,加油!If you find a path with no obstacles, it probably doesn’t
- Redis学习笔记(十三)redis配置文件redis.conf中文版(基于2.4)
- webdataBase