【jzoj4784】【Map】【强连通分量】

22 篇文章 0 订阅

题目大意

给出一个图,一些询问加入一条边后增加多少对双连通点对(u,v)(v,u)算两对。

解题思路

tarjan后建树,答案即为(u,v)路径上size和的平方减去平方和,倍增即可。

code

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define max(n1,n2) ((n1>n2)?n1:n2)
#define min(n1,n2) ((n1>n2)?n2:n1)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=200000,maxm=400000,inf=2147483647;
int n,m,q,lo,time,top,gra=2,to[maxm*2+10],next[maxm*2+10],begin[maxn+10],dfn[maxn+10],
low[maxn+10],inst[maxn+10],st[maxn+10],bel[maxn+10],fat[maxn+10][20],
dep[maxn+10],two[maxn+10],x[maxn*2+10],y[maxn*2+10];
LL sx[maxn+10][20],sxx[maxn+10][20],size[maxn+10];
void insert(int u,int v){
    to[gra]=v;
    next[gra]=begin[u];
    begin[u]=gra++;
    to[gra]=u;
    next[gra]=begin[v];
    begin[v]=gra++;
}
void tarjan(int now,int pre){
    dfn[now]=low[now]=++time;
    inst[st[++top]=now]=1;
    for(int i=begin[now];i;i=next[i])
        if(i!=(pre^1)){
            if(!dfn[to[i]]){
                tarjan(to[i],i);
                low[now]=min(low[now],low[to[i]]);
            }else if(inst[to[i]])low[now]=min(low[now],dfn[to[i]]);
        }
    if(dfn[now]==low[now]){
        for(;st[top]!=now;bel[st[top]]=now,size[now]++,inst[st[top--]]=0);
        bel[st[top]]=now,size[now]++,inst[st[top--]]=0;
    }
}
LL lc(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    LL ax=0,axx=0;
    fd(i,lo,0)
        if(dep[fat[u][i]]>=dep[v])
            ax+=sx[u][i],
            axx+=sxx[u][i],
            u=fat[u][i];
    if(u==v)return (ax+sx[u][0])*(ax+sx[u][0])-(axx+sxx[u][0]);
    fd(i,lo,0)
        if(fat[u][i]!=fat[v][i])
            ax+=sx[u][i]+sx[v][i],
            axx+=sxx[u][i]+sxx[v][i],
            u=fat[u][i],
            v=fat[v][i];
    ax+=sx[fat[u][0]][0]+sx[u][0]+sx[v][0];
    axx+=sxx[fat[u][0]][0]+sxx[u][0]+sxx[v][0];
    return ax*ax-axx;
}
int read(){
    int v=0;char ch=getchar();
    for(;(ch<'0')||(ch>'9');ch=getchar());
    for(;(ch>='0')&&(ch<='9');v=v*10+ch-'0',ch=getchar());
    return v;
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    n=read();m=read();q=read();
    fo(i,1,m){
        x[i]=read();y[i]=read();
        insert(x[i],y[i]);
    }
    fo(i,1,n)
        if(!dfn[i])tarjan(i,0);
    fo(i,1,n)sx[i][0]=size[bel[i]],sxx[i][0]=size[bel[i]]*size[bel[i]];
    lo=log(n)/log(2);
    fo(i,1,m)
        if(bel[x[i]]!=bel[y[i]])
            fat[bel[y[i]]][0]=bel[x[i]];
    fo(i,1,n)if(!fat[i][0])dep[i]=1;
    fo(i,1,n)if(fat[i][0])dep[i]=2;
    two[0]=1;fo(i,1,lo)two[i]=two[i-1]*2;
    fo(j,1,lo)
        fo(i,1,n){
            sx[i][j]=sx[i][j-1]+sx[fat[i][j-1]][j-1],
            sxx[i][j]=sxx[i][j-1]+sxx[fat[i][j-1]][j-1],
            fat[i][j]=fat[fat[i][j-1]][j-1];
            if(fat[i][j])dep[i]=dep[fat[i][j]]+two[j];
        }
    LL ans=0;
    fo(i,1,q){
        int x=read(),y=read();
        if(bel[x]!=bel[y])ans+=lc(bel[x],bel[y]);
    }
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值