HDU 5739 点-双联通分量

来源:互联网 发布:java中时间格式 编辑:程序博客网 时间:2024/04/29 23:56

官方题解其实说的很好的……


只不过我没弄明白连通分量的内在含义。弄明白以后,用刘汝佳大白书的模板即可。


这道题涉及逆元,因为读入的数据都比1e9小,所以直接用费马小定理的方法求逆元比较方便。


2个点,也算一个点连通分量。

1个点孤立的,在刘汝佳的模板中,不算连通分量,并且被记为属于0号连通快(其他正常连通块,都是编号从1开始往后一直到bcccnt的)。



然后,写之前想了好多……后来还是模仿某个菊苣的写法啦。




这张图看了好多遍不明白为啥1,2是一个连通分量……实际上应该是一个连通分量。


小技巧:对于求的点,为新的图的根的话,要考虑情况,所以在建树的时候,直接用添加的虚点为根,就比较稳了~程序也少一些东西。


#include <cstdio>#include <stack>#include <cstring>#include <algorithm>#include <iostream>using namespace std;typedef long long LL;const int maxn = 300000 + 10;const LL mod = 1e9 + 7;int n , m;struct Edge { int u, v; };int pre[maxn], iscut[maxn], bccno[maxn], dfsClock, bccCnt; // bccno[i]表示i属于哪一个bcc,割顶的bccno无意义vector<int> G[maxn], bcc[maxn];//bcc[i]保存每个分量的点stack<Edge> S;int dfs(int u, int fa);void findBcc(int n) {// 调用结束后S保证为空,所以不用清空memset(pre, 0, sizeof(pre));memset(iscut, 0, sizeof(iscut));memset(bccno, 0, sizeof(bccno));dfsClock = bccCnt = 0;for (int i = 0; i < n; i++) {if (!pre[i]) dfs(i, -1);}}LL w[maxn]; bool vis[maxn];void init(){scanf("%d%d", &n, &m);for (int i = 0; i < n; ++ i){scanf("%lld", &w[i]);G[i].clear();bcc[i].clear();}while (m--){int a, b;scanf("%d%d", &a, &b);--a;--b;G[a].push_back(b);G[b].push_back(a);}}LL powMod( LL a , LL b , LL p  = mod)//a^b % p  {      LL r = 1 ;      a %= p ;      while( b )      {          if( b&1 ) r = r*a%p ;          b >>= 1 ;          a = a*a%p ;      }      return r ;  }    vector<int>g[maxn];LL sum[maxn], mul[maxn];int belong[maxn];#define prln(x) cout<<#x<<" = "<<x<<endl#define pr(x) cout<<#x<<" = "<<xvoid dp(int cur, int root, int fa){vis[cur] = true;sum[cur] = 0;belong[cur] = root;mul[cur] = w[cur];//pr(cur),prln(mul[cur]);for (auto will : g[cur]){//pr(cur);//prln(will);if (will == fa)continue;dp(will, root, cur);sum[cur] = (sum[cur] + mul[will]) % mod;mul[cur] = mul[cur] * mul[will] % mod;}}void doit(){findBcc(n);for (int i = 0; i <= n + bccCnt; ++ i)g[i].clear();for (int i = 1; i <= bccCnt; ++ i){//prln(i);//第i个点-连通分量的虚点,是n+i-1//最后一个编号显然是n+bccnt-1int fake_vertex = n + i - 1;w[fake_vertex]= 1;for (auto x : bcc[i]){g[fake_vertex].push_back(x);g[x].push_back(fake_vertex);}}//构图完毕memset(vis, 0, sizeof(vis));LL tot = 0;//总结果for (int i = 0; i < n + bccCnt; ++ i){if (!vis[i]){if (!iscut[i])//确保树的根,不是割点。{dp(i, i, -1);tot = (tot + mul[i]) % mod;//cout<<"!!!"<<endl;}}}LL ans=0;for (int i = 0; i < n; ++ i){LL ith_ans;//这一轮的答案if (!g[i].size())//孤立点{ith_ans = (tot-w[i]+mod)%mod;}else if (!iscut[i]){LL ni = powMod(w[i], mod - 2);ith_ans = tot- mul[belong[i]] + mul[belong[i]]*ni % mod + mod;ith_ans %= mod;}else{//一定不再根if (belong[i] == i){cout<<"root error!"<<endl;exit(0);}LL ni = powMod(mul[i], mod - 2);ith_ans = tot - mul[belong[i]];ith_ans += mul[belong[i]] * ni + sum[i] + mod;ith_ans %= mod;}//prln(ith_ans);ans = ans + (i+1LL) * ith_ans % mod;ans %= mod;}printf("%lld\n", ans);}int main(){int T;scanf("%d", &T);while (T--){init();doit();}return 0;}int dfs(int u, int fa) {int lowu = pre[u] = ++dfsClock;int child = 0;for (int i = 0; i < G[u].size(); i++) {int v = G[u][i];Edge e = (Edge){u, v};if (!pre[v]) { // 没有访问过vS.push(e);child++;int lowv = dfs(v, u);lowu = min(lowu, lowv); // 用后代的low函数更新自己if (lowv >= pre[u]) {iscut[u] = true;bccCnt++; bcc[bccCnt].clear();//注意!bcc从1开始编号for (;;) {Edge x = S.top(); S.pop();if (bccno[x.u] != bccCnt) { bcc[bccCnt].push_back(x.u); bccno[x.u] = bccCnt; }if (bccno[x.v] != bccCnt) { bcc[bccCnt].push_back(x.v); bccno[x.v] = bccCnt; }if (x.u == u && x.v == v) break;}}continue;}if (pre[v] < pre[u] && v != fa) {S.push(e);lowu = min(lowu, pre[v]); // 用反向边更新自己}}if (fa < 0 && child == 1) iscut[u] = 0;return lowu;}


0 0
原创粉丝点击