[2016ACM多校] HDU5739 搜索
2016-07-22 23:08
211 查看
题意
给一个无向图G,N个点,删去一个点i后得到Gi的重Zi为Gi所有仍然联通的分量的内部点权值乘积再求和。思路
CDQ暴力回滚并查集,可解决。这里使用tarjan算法,挺坑的。依次搜索每个没被搜索过的点,进入该点几下时间戳,看能否访问到时间戳更早的点,如果不能这个点就是割点。对于不是割点的点很好办,联通分量的累乘积乘以它自己的逆元再加上其他分量的乘积。为了计算割点,在搜索的时候全局记录一个到目前为止经过点的乘积mul,当从该点进入一个分支时局部记录当前的nowmul,如果回溯的时候发现这个点是割点那么这个分支的累乘结果就是mul/nowmul,把每个分支的累加上,再记录一个各个分支的累乘值cz[i],最后再用连通分量的累乘值乘以cz[i]*w[i]的逆元。就是该点父亲方向的结果。全加起来再加上其他分量的值就是割点的zi。这里关键是有两个坑。只有一个点的连通分量再被删去该点时,联通分量的值要记0,统一算会成1,还有就是搜索的进入点是没有父亲分支的,如果用逆元算也会多1。
如果套点双连通分量的板子的话把对双连通分量的记录改成对一般联通分量的记录就可以了,别忘了特判搜索起点。
AC代码 C++
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <ctype.h> #include <iostream> #include <algorithm> #include <vector> #include <stack> #include <queue> #include <deque> #include <set> #include <map> using namespace std; #define MAXN 100005 #define MOD 1000000007 int inv(int x) { int res=1, mod=MOD-2; do { if(mod & 1) res = (long long)res * x % MOD; x = (long long)x * x % MOD; }while(mod >>= 1); return res; } vector<int> G[MAXN]; int w[MAXN]; int z[MAXN]; int cz[MAXN]; int pre[MAXN]; bool iscut[MAXN]; int ncc_no[MAXN]; vector<int> ncc[MAXN]; int ncc_size[MAXN]; int ncc_cnt; int clock; int tarjan(int u, int fa) { ncc[ncc_no[u] = ncc_cnt].push_back(u); ncc_size[ncc_cnt] = ((long long)ncc_size[ncc_cnt] * w[u]) % MOD; int lowu = pre[u] = ++clock; int child, i, v, lowv, nowsize, tmp; for(child=0, i=G[u].size(); i--;) { if(!pre[v = G[u][i]]) { child++; nowsize = ncc_size[ncc_cnt]; lowv = tarjan(v, u); lowu = min(lowv, lowu); if(lowv >= pre[u]) { iscut[u] = true; tmp = (long long)ncc_size[ncc_cnt] * inv(nowsize) % MOD; z[u] = (z[u] + tmp) % MOD; cz[u] = (long long)cz[u] * tmp % MOD; } } else if(pre[v] < pre[u] && v != fa) lowu = min(lowu, pre[v]); } if(fa < 0 && child == 1) iscut[u] = false; return lowu; } int main() { int t, n, m, x, y, i, sum; scanf("%d", &t); while(t-- && scanf("%d%d", &n, &m) > 0) { for(i=1; i<=n; i++) { scanf("%d", w+i); G[i].clear(); cz[i] = 1; ncc[i].clear(); } while(m--) { scanf("%d%d", &x, &y); G[x].push_back(y); G[y].push_back(x); } memset(z, 0, sizeof z); memset(pre, 0, sizeof pre); memset(iscut, false, sizeof iscut); memset(ncc_no, 0, sizeof ncc_no); clock = ncc_cnt = 0; for(i=1, sum=0; i<=n; i++) if(!pre[i]) { ncc_size[++ncc_cnt] = 1; tarjan(i, -1); for(x=ncc[ncc_cnt].size(); x--;) if(iscut[y = ncc[ncc_cnt][x]] && y!=i) z[y] = (z[y] + (long long)ncc_size[ncc_cnt] * inv((long < 4000 span class="hljs-keyword">long)cz[y] * w[y] % MOD) % MOD) % MOD; sum = (sum + ncc_size[ncc_cnt]) % MOD; } for(i=1, y=0; i<=n; i++) { if(!iscut[i]) z[i] = ncc[ncc_no[i]].size() > 1 ? (long long)ncc_size[ncc_no[i]] * inv(w[i]) % MOD : 0; y = ((long long)y + ((long long)z[i] + (long long)(sum - ncc_size[ncc_no[i]] + MOD) % MOD) % MOD * i % MOD) % MOD; } printf("%d\n", y); } return 0; }
相关文章推荐
- 搜狗百度360市值齐跌:搜索引擎们陷入集体焦虑?
- 本人即将筹备败家日志,敬请期待!
- 破墙而入看电视
- 书评:《算法之美( Algorithms to Live By )》
- IE:使用搜索助手
- 动易2006序列号破解算法公布
- C#递归算法之分而治之策略
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- C#算法之大牛生小牛的问题高效解决方法
- C#算法函数:获取一个字符串中的最大长度的数字
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- 经典排序算法之冒泡排序(Bubble sort)代码
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法