HDU 5739(点双连通)


题目大意:给定一个无向图,对于每个连通分量的权值为其中点的权值乘积,而整个无向图的权值为所以连通分量的权值和。Gi表示删除点i,图的权值,求sum(i*Gi)%(10e+7)


显然只有删除割点才会改变图的连通性,但我也没有想到怎么解决,这里根据官方题解

显然, 只要删掉关键点才会使图不联通. 对于其他点, 权值很容易计算.

首先求出所有的点双联通分量, 对于每一个点双联通分量SSS, 新建一个节点sss, 向SSS中每个节点vvv连边. 这样一来, 新增的点和原来图中的点会构成一个森林(据说这个有个名字, block forest data structure). 很容易观察到, 叶子节点肯定都是非关键点, 内部节点要么是关键点, 要么是新增的节点.

对于这个森林FFF, 删掉一个关键点或者一个叶子iii之后, 会得到一个新森林FiF_iFi, 这个FiF_iFi对应的连通块集合和GiG_iGi对应的连通块集合其实是一样的(不考虑那些新增的点). 显然GiG_iGi的权值和FiF_iFi的权值也是一样的, FiF_iFi的权值我们很容易通过树形dp算出来, 那么GiG_iGi的权值也随之而出.

根据对应性质,先求出点双连通分量,再进行树形dp即可。开始求点双连通时用了vector超时,改了就没问题了。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <ctime>

using namespace std;

#define ll long long

const int maxn=800005;
const ll mod=1000000007;

ll pri[maxn];
int head[maxn];
int to[maxn],ne[maxn],from[maxn];
int cot;

inline void addedge(int u,int v){
    from[cot]=u;
    to[cot]=v;
    ne[cot]=head[u];
    head[u]=cot++;
}

int head1[maxn];
int to1[maxn];
int ne1[maxn];
int cot1;
inline void addEdge(int u,int v){
    to1[cot1]=v;
    ne1[cot1]=head1[u];
    head1[u]=cot1++;
}

ll pow_mod(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1)(res*=a)%=mod;
        (a*=a)%=mod;
        b>>=1;
    }
    return res;
}

int dfn[maxn];
int dfs_cot;
int bcc_cot;
int st[maxn];
int top;
bool vis[maxn];
int dfs(int u,int fa){
    vis[u]=true;
    int lowu=dfn[u]=++dfs_cot;
    for(int i=head[u];~i;i=ne[i]){
        int v=to[i];
        if(!vis[v]){
            st[top++]=i;
            int lowv=dfs(v,u);
            lowu=min(lowv,lowu);
            if(lowv>=dfn[u]){
                bcc_cot++;
                //printf("bcc[%d]:\n",bcc_cot);
                for(;;){
                    if(top==0)break;
                    int e=st[--top];
                    //printf("e[%d]:%d %d\n",e,from[e],to[e]);
                    //if(bcc_id[from[e]]!=bcc_cot)printf("push:%d\n",from[e]);
                    addEdge(bcc_cot,from[e]);
                    addEdge(from[e],bcc_cot);
                    //if(bcc_id[to[e]]!=bcc_cot)printf("push:%d\n",to[e]);
                    addEdge(bcc_cot,to[e]);
                    addEdge(to[e],bcc_cot);
                    pri[bcc_cot]=1;
                    if(from[e]==u&&to[e]==v)break;
                }
            }
        }
        else if(dfn[v]<dfn[u]&&v!=fa){
            st[top++]=i;
            lowu=min(lowu,dfn[v]);
        }
    }
    return lowu;
}

void init(int n){
    bcc_cot=n;
    top=0;
    cot=0;
    dfs_cot=0;
    cot1=0;
    memset(head1,-1,sizeof(head1));
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
}

ll sum[maxn];
ll product[maxn];
void dp_dfs(int u,int fa){
    vis[u]=true;
    sum[u]=0;
    product[u]=pri[u];
    for(int i=head1[u];~i;i=ne1[i]){
        int v=to1[i];
        if(v==fa)continue;
        if(!vis[v]){
            dp_dfs(v,u);
            sum[u]=(sum[u]+product[v])%mod;
            product[u]=(product[v]*product[u])%mod;
        }
    }
}

void dp(int u,int fa){
    dfn[u]=1;
    if(fa!=0){
        int tmp=product[fa]*pow_mod(product[u],mod-2)%mod;
        sum[u]=(sum[u]+tmp)%mod;
        product[u]=product[fa];
    }
    for(int i=head1[u];~i;i=ne1[i]){
        int v=to1[i];
        if(!dfn[v]){
            dp(v,u);
        }
    }
}

int main(){
    int t,n,m;
    int u,v;
    scanf("%d",&t);
    while(t--){
        scanf("%d %d",&n,&m);
        init(n);
        for(int i=1;i<=n;i++){
            scanf("%lld",&pri[i]);
            pri[i]%=mod;
        }
        cot=0;
        for(int i=0;i<m;i++){
            scanf("%d %d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        for(int i=1;i<=n;i++){
            if(!vis[i])dfs(i,0);
        }
        memset(vis,0,sizeof(vis));
        memset(dfn,0,sizeof(dfn));
        ll ans=0;
        for(int i=1;i<=n;i++){
            if(!vis[i]){
                dp_dfs(i,0);
                dp(i,0);
                (ans+=product[i])%=mod;
            }
        }
/*
        for(int i=1;i<=bcc_cot;i++){
            printf("sum[%d]:%lld\n",i,sum[i]);
            printf("pro[%d]:%lld\n",i,product[i]);
        }
        printf("ans:%lld\n",ans);
*/
        ll fans=0;
        for(int i=1;i<=n;i++){
            (fans+=(i*(ans+sum[i]-product[i]+mod))%mod)%=mod;
        }
        printf("%lld\n",fans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值