【JZOJ 4784】 Map

4 篇文章 0 订阅
3 篇文章 0 订阅

Description

这里写图片描述
这里写图片描述

Analysis

又是一道裸题,唉
双连通分量缩点之后在树上维护一些东西做LCA,怎么看都是练码力啊。
我好6啊3周连续3道连通分量类的题全部自信不对拍全部比赛时一次AC,23333
这说明自己的码力有所提升了,是好事
但是不对拍是不对的,还是要求稳,万一正式比赛翻车了就后悔莫及了。

Code

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
#define efo(i,v) for(int i=last[v];i;i=next[i])
using namespace std;
typedef long long ll;
const int N=200010,M=800010;
int n,m,tot,lyd,to[M],next[M],last[N];
int num,now,top,sta[N],low[N],dfn[N],bz[N],c[N];
int f[N][20],dep[N];
ll s1,s2,g[N][20],h[N][20],size[N];
int fa[N];
struct edge
{
    int x,y;
}b[M];
bool vis[N],p[M];
void link(int u,int v)
{
    to[++tot]=v,next[tot]=last[u],last[u]=tot;
}
void tarjan(int v)
{
    sta[++top]=v;
    low[v]=dfn[v]=++now;
    bz[v]=lyd;
    vis[v]=1;
    efo(i,v)
    {
        if(p[i]) continue;
        int u=to[i];
        if(!dfn[u])
        {
            p[i]=p[i^1]=1;
            tarjan(u);
            p[i]=p[i^1]=0;
            low[v]=min(low[v],low[u]);
        }
        else
        if(vis[u]) low[v]=min(low[v],dfn[u]);
    }
    if(low[v]==dfn[v])
    {
        num++;
        for(;sta[top+1]!=v;) c[sta[top--]]=num,size[num]++;
    }
}
int getfa(int v)
{
    if(!fa[v]) return v;
    else return fa[v]=getfa(fa[v]);
}
void dfs(int v,int fr,int d)
{
    dep[v]=d,f[v][0]=fr,g[v][0]=size[fr],h[v][0]=size[fr]*(size[fr]-1);
    efo(i,v)
    {
        int u=to[i];
        if(u==fr) continue;
        dfs(u,v,d+1);
    }
}
void jump(int &u,int i)
{
    s1+=g[u][i],s2+=h[u][i],u=f[u][i];
}
ll lca(int u,int v)
{
    s1=s2=0;
    if(dep[u]<dep[v]) swap(u,v);
    int x=u,y=v;
    fd(i,log2(dep[u]),0)
        if(dep[f[u][i]]>=dep[v]) jump(u,i);
    if(u==v)
    {
        s1+=size[x],s2+=size[x]*(size[x]-1);
        return s1*(s1-1)-s2;
    }
    fd(i,log2(dep[u]),0)
        if(f[u][i]!=f[v][i]) jump(u,i),jump(v,i);
    jump(u,0),jump(v,0);
    s1+=size[x]+size[y]-size[u];
    s2+=size[x]*(size[x]-1)+size[y]*(size[y]-1)-size[u]*(size[u]-1);
    return s1*(s1-1)-s2;
}
int main()
{
    int _,u,v;
    ll ans=0;
    scanf("%d %d %d",&n,&m,&_);
    tot=1;
    fo(i,1,m)
    {
        scanf("%d %d",&u,&v);
        b[i].x=u,b[i].y=v;
        link(u,v),link(v,u);
    }
    fo(i,1,n)
        if(!bz[i])
        {
            while(top) sta[top--]=0;
            ++lyd;
            tarjan(i);
        }
    tot=0;
    memset(last,0,sizeof(last));
    fo(i,1,m)
    {
        u=c[b[i].x],v=c[b[i].y];
        if(u==v) continue;
        int fu=getfa(u),fv=getfa(v);
        if(fu==fv) continue;
        fa[v]=u;
        link(u,v),link(v,u);
    }
    dfs(1,0,1);
    fo(j,1,log2(n))
        fo(i,1,n)
        {
            f[i][j]=f[f[i][j-1]][j-1];
            g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1];
            h[i][j]=h[i][j-1]+h[f[i][j-1]][j-1];
        }
    while(_--)
    {
        scanf("%d %d",&u,&v);
        u=c[u],v=c[v];
        if(u==v) continue;
        ans+=lca(u,v);
    }
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值