【HDU 5739】Fantasia(点双连通+dfs)

【HDU 5739】Fantasia(点双连通+dfs)

Fantasia


Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1240    Accepted Submission(s): 314

Problem 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.

The weight of a graph G is defined as follows:

  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.

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.

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:

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.

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

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

There are at most 1000 test cases and n,m1.5×106 .

Output
For each test case, output an integer S=(i=1nizi)mod(109+7) , where zi is the weight of Gi.

Sample Input
1
3 2
1 2 3
1 2
2 3

Sample Output
20

Author
zimpha

Source
2016 Multi-University Training Contest 2

题目大意:
给出一个无向图 G (不保证图连通)和图中每个点的权值。
定义zi为从图 G 中删掉点i后的图Gi的重量。

定义图 G 的重量为:
如果G是连通图,则重量为图中各点权值的乘积
如果G不是连通图,则重量为所有最大连通图的重量的加和。

S=(i=1nizi)mod(109+7)

假设G初始为连通图,那么除了割点,其余点的 zi 为图中各点乘积(除了i点)
这样跑个Tarjan,对于同一个双连通图中的点,开一个新点,并把他们与新点连接一条边。做完后会变成一个新图(抛去旧边),或者说是一棵树。
叶子节点都是非割点,非叶子节点要么是新家的点,要么是割点。

这样搜一遍新建的树就能把S求出来了。因为图 G <script type="math/tex" id="MathJax-Element-1513">G</script>初始未必是连通图
因此需要处理出来每个连通图的乘积。然后挨个图搜索,具体时间就不细述了,主要是Tarjan和建新图(树)那里。

代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;

struct Edge
{
    int v,next;
};

Edge eg[1666666];
int nd[233333];
int head[233333];
int val[233333];
bool vis[233333];
int dfn[233333],low[233333];
int tp,n,on,tim;
stack <int> s;

void Add(int u,int v,int *hd)
{
    eg[tp].v = v;
    eg[tp].next = hd[u];
    hd[u] = tp++;
}

LL sum[233333];
LL all;
void init()
{
    while(!s.empty()) s.pop();
    memset(head,-1,sizeof(head));
    memset(nd,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    tim = tp = 0;
    int m;
    scanf("%d%d",&n,&m);
    //printf("%d %d\n",n,m);

    for(int i = 1; i <= n; ++i) 
        scanf("%d",&val[i]);

    int u,v;
    while(m--)
    {
        scanf("%d%d",&u,&v);
        Add(u,v,head);
        Add(v,u,head);
    }
}

void Pop(int u,int pre)
{
    int v;

    n++;
    do
    {
        v = s.top();
        s.pop();
    //  printf("tp:%d\n",tp);
        Add(v,n,nd);
        Add(n,v,nd);
    }while(v != u);

    //  printf("tp:%d\n",tp);
    Add(pre,n,nd);
    Add(n,pre,nd);
    val[n] = 1;
}

void Tarjan(int u,int root)
{
    sum[root] = (sum[root]*val[u])%mod;
    vis[u] = 1;
    s.push(u);
    dfn[u] = low[u] = tim++;

    int v;
    for(int i = head[u]; i != -1; i = eg[i].next)
    {
        v = eg[i].v;
        if(!vis[v])
        {
            Tarjan(v,root);
            low[u] = min(low[u],low[v]);

            if(low[v] >= dfn[u])
            {
                Pop(v,u);
            }
        }
        else low[u] = min(low[u],dfn[v]);
    }
}

LL pow_m(LL a,int b)
{
    LL ans = 1;
    while(b)
    {
        if(b&1) ans = (ans*a)%mod;
        b >>= 1;
        a = (a*a)%mod;
    }
    return ans;
}

LL ans;
LL mul[233333];

void dfs(int u,int root,LL oth)
{
    //printf("inu:%d\n",u);
    mul[u] = val[u];
    vis[u] = 1;
    int v;
    LL cnt = 0;
    for(int i = nd[u]; i != -1; i = eg[i].next)
    {
        //printf("u:%d pre:%d\n",u,pre);
        v = eg[i].v;
        //printf("%d:%d-%d\n",i,u,v);
        if(vis[v]) continue;
        dfs(v,root,oth);
        mul[u] = (mul[u]*mul[v])%mod;
        if(u <= on) cnt = (cnt+mul[v])%mod;
    }
    //printf("outu:%d\n",u);

    if(u <= on) 
    {
        if(u == root) ans = (ans+((u*((cnt+oth)%mod))%mod))%mod;
        else ans = (ans+((u*((cnt+((sum[root]*pow_m(mul[u],mod-2))%mod)+oth)%mod))%mod))%mod;
        //printf("sum:%lld cnt:%lld mul:%lld\n",sum,cnt,mul[u]);
        //printf("%d*%lld\n",u,((((cnt+((sum*pow_m(mul[u],mod-2))%mod))%mod))%mod));
    }
    //printf("%d=%lld\n",u,cnt);
}

int main()
{
//  fread("data1.in");
//  fwrite("test.out");

    int t;

    scanf("%d",&t);

    while(t--)
    {
        //printf("%d\n",t);
        init();
        on = n;
        all = 0;
        for(int i = 1; i <= on; ++i)
        {
            if(vis[i]) continue;
            sum[i] = 1;
            Tarjan(i,i);
            all = (all+sum[i])%mod;
        }
        //printf("%d %d\n",n,tp);


        memset(vis,0,sizeof(vis));
        ans = 0;
        for(int i = 1; i <= n; ++i)
        {
            if(vis[i]) continue;
            dfs(i,i,(((all-sum[i])%mod)+mod)%mod);
        }
        printf("%lld\n",ans);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值