HDU5739 Fantasia(點雙連通份量 + Block Forest Data Structure)

題目

Source

http://acm.hdu.edu.cn/showproblem.php?pid=5739php

Description

Professor Zhang has an undirected graph G with n vertices and m edges. Each vertex is attached with a weight wi. Let Gi be the graph after deleting the i-th vertex from graph G. Professor Zhang wants to find the weight of G1,G2,...,Gn.html

The weight of a graph G is defined as follows:spa

1. If G is connected, then the weight of G is the product of the weight of each vertex in G.
2. Otherwise, the weight of G is the sum of the weight of all the connected components of G.rest

A connected component of an undirected graph G is a subgraph in which any two vertices are connected to each other by paths, and which is connected to no additional vertices in G.component

Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:htm

The first line contains two integers n and m (2≤n≤105,1≤m≤2×105) -- the number of vertices and the number of edges.blog

The second line contains n integers w1,w2,...,wn (1≤wi≤109), denoting the weight of each vertex.ip

In the next m lines, each contains two integers xi and yi (1≤xi,yi≤n,xi≠yi), denoting an undirected edge.get

There are at most 1000 test cases and ∑n,∑m≤1.5×106.input

Output

For each test case, output an integer $S = (\sum\limits_{i=1}^{n}i\cdot z_i) \text{ mod } (10^9 + 7)$, where zi is the weight of Gi.

Sample Input

1
3 2
1 2 3
1 2
2 3

Sample Output

20

 

分析

題目大概說給一張無向點帶有權無向圖。定義連通圖的權值爲圖中各點權的乘積,圖的權值爲其包含的各連通圖的權和。設$z_i$爲刪除i點後圖的權值,求$S = (\sum\limits_{i=1}^{n}i\cdot z_i) \text{ mod } (10^9 + 7)$。

 

官方題解這麼說的:

顯然, 只要刪掉關鍵點纔會使圖不聯通. 對於其餘點, 權值很容易計算.

首先求出全部的點雙聯通份量, 對於每個點雙聯通份量$S$, 新建一個節點$s$, 向$S$中每一個節點$v$連邊. 這樣一來, 新增的點和原來圖中的點會構成一個森林(聽說這個有個名字, block forest data structure). 很容易觀察到, 葉子節點確定都是非關鍵點, 內部節點要麼是關鍵點, 要麼是新增的節點.

對於這個森林$F$, 刪掉一個關鍵點或者一個葉子$i$以後, 會獲得一個新森林$F_i$​​, 這個$F_i$​​對應的連通塊集合和$G_i$對應的連通塊集合實際上是同樣的(不考慮那些新增的點). 顯然$G_i$的權值和$F_i$​​的權值也是同樣的, $F_i$的權值咱們很容易經過樹形dp算出來, 那麼$G_i$的權值也隨之而出.

能夠在網上搜到關於用那個BF在線性時間計算全部關節點的影響的論文。。裏面有這麼一張圖:

這樣就好理解了。

設新加圓形結點的權爲1,在那棵構造出來的樹中用dp求出各個結點的兩個信息:

  • pro[u]表示u爲根的子樹內各個結點權值的乘積
  • sum[u]表示Σpro[v](u爲本來圖中的結點,v爲u的孩子結點)

最後經過枚舉要刪除的各個點,再加上乘法逆元搞搞,就能直接經過這兩個信息很快地求出刪除某結點後新的總權值。

另外。。有個地方空間開過小WA了很久。。

 

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 111111
#define MAXM 222222

struct Edge{
    int v,flag,next;
}edge[MAXM<<1];
int NE,head[MAXN];
void addEdge(int u,int v){
    edge[NE].v=v; edge[NE].flag=0; edge[NE].next=head[u];
    head[u]=NE++;
}

struct TEdge{
    int v,next;
}tEdge[MAXM<<4];
int tNE,tHead[MAXN<<1];
void addEdge(int u,int v,int nothing){
    tEdge[tNE].v=v; tEdge[tNE].next=tHead[u];
    tHead[u]=tNE++;
}

int dn,dfn[MAXN],low[MAXN];
int stack[MAXM],top;
int root[MAXN],rn;

void tarjan(int u,int rt){
    dfn[u]=low[u]=++dn;
    for(int i=head[u]; i!=-1; i=edge[i].next){
        if(edge[i].flag) continue;
        edge[i].flag=edge[i^1].flag=1;
        stack[++top]=i;

        int v=edge[i].v;

        if(dfn[v]){
            low[u]=min(low[u],dfn[v]);
            continue;
        }

        tarjan(v,rt);
        low[u]=min(low[u],low[v]);

        if(low[v]>=dfn[u]){
            ++rn;
            int k;
            do{
                k=stack[top--];
                root[edge[k].v]=rt;
                root[edge[k^1].v]=rt;
                addEdge(rn,edge[k].v,0);
                addEdge(edge[k].v,rn,0);
                addEdge(rn,edge[k^1].v,0);
                addEdge(edge[k^1].v,rn,0);
            }while(edge[k^1].v!=u);
        }
    }
}

int n,weight[MAXN];

bool vis[MAXN<<1];
long long sum[MAXN<<1],pro[MAXN<<1];
void dfs(int u){
    vis[u]=1;
    sum[u]=0; pro[u]=(u<=n) ? weight[u] : 1;
    for(int i=tHead[u]; i!=-1; i=tEdge[i].next){
        int v=tEdge[i].v;
        if(vis[v]) continue;
        dfs(v);
        if(u<=n){
            sum[u]+=pro[v];
            sum[u]%=1000000007;
        }
        pro[u]*=pro[v];
        pro[u]%=1000000007;
    }
}

long long ine(long long x){
    long long res=1;
    int n=1000000007-2;
    while(n){
        if(n&1){
            res*=x; res%=1000000007;
        }
        x*=x; x%=1000000007;
        n>>=1;
    }
    return res;
}

int main(){
    int t,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; ++i){
            scanf("%d",weight+i);
        }

        NE=0;
        memset(head,-1,sizeof(head));
        int a,b;
        while(m--){
            scanf("%d%d",&a,&b);
            addEdge(a,b);
            addEdge(b,a);
        }

        dn=0; memset(dfn,0,sizeof(dfn));
        rn=n; memset(root,0,sizeof(root));
        top=0;
        tNE=0; memset(tHead,-1,sizeof(tHead));
        for(int i=1; i<=n; ++i){
            if(dfn[i]==0) tarjan(i,rn+1);
        }

        long long tot=0;

        memset(vis,0,sizeof(vis));
        for(int i=1; i<=n; ++i){
            if(vis[i]) continue;
            if(root[i]){
                dfs(root[i]);
                tot+=pro[root[i]];
                tot%=1000000007;
            }else{
                tot+=weight[i];
                tot%=1000000007;
            }
        }

        long long ans=0;

        for(int i=1; i<=n; ++i){
            if(root[i]){
                ans+=(tot-pro[root[i]]+pro[root[i]]*ine(pro[i])%1000000007+sum[i])%1000000007*i;
                ans%=1000000007;
            }else{
                ans+=(tot-weight[i])*i;
                ans%=1000000007;
            }
        }

        if(ans<0) ans+=1000000007;
        printf("%lld\n",ans);
    }
    return 0;
}
相關文章
相關標籤/搜索