HDU5739 Fantasia【点双连通分量 割点】

HDU5739 Fantasia

题意:

给出一张\(N\)个点的无向图\(G\),每个点都有权值\(w_i\),要求计算\(\sum_{i=1}^{N}i\cdot G_i % 1e9+7\)
其中\(G_i\)为删掉点\(i\)之后剩下各连通块内点权乘积之和

题解:

显然对于不是割点的点很容易计算出答案
对于割点,我们需要知道删掉这个点之后产生的新的连通块的点权乘积和
\(tarjan\)过程中可以直接处理出各联通子图的点权乘积(除了父节点所在的子图)
而父节点所在子图的点权乘积可以用整张图的点权乘积去除掉除它以外的点的点权乘积
具体实现看代码

view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
typedef long long int LL;
const int MAXN = 2e5+7;
const LL MOD = 1e9+7;
int n,m,bccid[MAXN],dfn[MAXN],low[MAXN],ID,w[MAXN],bel[MAXN],idx;
vector<int> G[MAXN],pt[MAXN];
LL tot,ans,f[MAXN],gw[MAXN],mul[MAXN],subsum[MAXN],submul[MAXN];
bool iscut[MAXN];
LL ksm(LL a, LL b){
    LL ret = 1;
    while(b){
        if(b&1) ret = ret * a % MOD;
        b >>= 1;
        a = a * a % MOD;
    }
    return ret;
}
LL inv(LL x){ return ksm(x,MOD-2); }
void init(){
    for(int i = 1; i <= n; i++) G[i].clear();
    memset(dfn+1,0,n<<2);
    memset(bel+1,0,n<<2);
    memset(iscut+1,0,n);
    fill(submul+1,submul+1+n,1);
    fill(subsum+1,subsum+1+n,0);
    ans = tot = ID = idx = 0;
}
void tarjan(int u, int par, int id){
    pt[id].push_back(u);
    bel[u] = id;
    dfn[u] = low[u] = ++idx;
    mul[id] = mul[id] * w[u] % MOD;
    int child = 0;
    for(int v : G[u]){
        if(v==par) continue;
        if(!dfn[v]){
            child++;
            LL tmp = mul[ID];
            tarjan(v,u,id);
            low[u] = min(low[u],low[v]);
            if(low[v]>=dfn[u]){
                if(par) iscut[u] = true;
                LL sub = mul[ID] * inv(tmp) % MOD;
                // 由于不确定根节点是否是割点,所以先当作割点来处理
                subsum[u] = (subsum[u] + sub) % MOD;
                submul[u] = submul[u] * sub % MOD;
            }
        }
        else low[u] = min(low[u],dfn[v]);
    }
    if(!par and child > 1) iscut[u] = true;
}
void solve(){
    scanf("%d %d",&n,&m);
    init();
    for(int i = 1; i <= n; i++) scanf("%d",&w[i]);
    for(int i = 1; i <= m; i++){
        int u, v; scanf("%d %d",&u,&v);
        G[u].push_back(v); G[v].push_back(u);
    }
    for(int i = 1; i <= n; i++) if(!dfn[i]){
        pt[++ID].clear();
        mul[ID] = 1;
        tarjan(i,0,ID);
        tot = (tot + mul[ID]) % MOD;
        for(int x : pt[ID]){
            if(x==i) continue;
            subsum[x] = (subsum[x] + mul[ID] * inv(submul[x]*w[x]%MOD) % MOD) % MOD;
        }
    }
    for(int i = 1; i <= n; i++){
        LL res = 0;
        if(iscut[i]) res = (tot - mul[bel[i]] + subsum[i] + MOD) % MOD;
        else{
            if(pt[bel[i]].size() == 1) res = (tot - w[i] + MOD) % MOD;
            else res = (tot - mul[bel[i]] + mul[bel[i]] * inv(w[i]) % MOD + MOD) % MOD;
        }
        ans = (ans + i * res) % MOD;
    }
    printf("%I64d\n",ans);
}
int main(){
    int tt;
    for(scanf("%d",&tt); tt; tt--) solve();
    return 0;
}
posted @ 2020-06-25 20:52  _kiko  阅读(123)  评论(0编辑  收藏  举报