【NOIP模拟】Map

来源:互联网 发布:人人商城数据备份 编辑:程序博客网 时间:2024/06/05 17:13

Description

这里写图片描述

Solution

这是一道水题。
首先对原图进行边的双连通分量,那么很显然,在两个不同的联通分量中的点对就是不安全的点对,同在相同的连通分量中的点对就是安全点对。
那么假如有两个连通分量互相连边,那么就会有一群连通分量合并为一个大的连通分量。
那么要如何实现呢?

tarjan缩点

这是一个很显然而且很经典的想法。
先对原图用tarjan缩点(每个点存储它点的个数),然后缩完点之后就会变成一个树。之后在这个树上倍增就能得到答案。
但是要怎么处理两两点相乘的和?

很简单的容斥(其实是很闲单的数学转换)

比如说现在有四个点的权值分别为a,b,c,d,要快速得到两两之间点的乘积,用:(a+b+c+d)2a2b2c2d2就可以了,而且还刚好是两倍。

Code

#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define rep(i,a) for(i=first[a];i;i=next[i])#define rep1(i,a) for(i=first1[a];i;i=next1[i])#define fod(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=400007;int i,j,k,l,t,n,m,cas;int first[maxn*2],next[maxn*2],last[maxn*2],num;int first1[maxn*2],next1[maxn*2],last1[maxn*2],num1;int low[maxn],dfn[maxn],stack[maxn],dfx,tou;int size[maxn],a[maxn],hhh;int b[maxn],c[maxn];bool az[maxn],bz[maxn],cz[maxn*2];ll ans,zhi,zhi1,g[maxn][21],p[maxn][21];int deep[maxn],f[maxn][21];void add(int x,int y){    last[++num]=y;next[num]=first[x],first[x]=num;}void add1(int x,int y){    last1[++num1]=y;next1[num1]=first1[x],first1[x]=num1;}void dfs1(int x,int y){    int i;    deep[x]=deep[y]+1,f[x][0]=y;    g[x][0]=size[y];p[x][0]=size[y]*size[y];    rep1(i,x){        if(last1[i]!=y){            dfs1(last1[i],x);        }    }}int lca(int x,int y){    int i;if(deep[x]<deep[y])swap(x,y);zhi=zhi1=0;    fod(i,20,0)if(deep[f[x][i]]>deep[y])zhi+=g[x][i],zhi1+=p[x][i],x=f[x][i];    if(deep[x]!=deep[y])zhi+=g[x][0],zhi1+=p[x][0],x=f[x][0];    fod(i,20,0)if(f[x][i]!=f[y][i])zhi+=g[x][i]+g[y][i],zhi1+=p[x][i]+p[y][i],x=f[x][i],y=f[y][i];    if(x!=y){        zhi+=g[x][0];        zhi1+=p[x][0];        return f[x][0];    }    return x;}void tarjan(int x){    int i;    dfn[x]=low[x]=++dfx;    stack[++stack[0]]=x;    bz[x]=az[x]=1;    rep(i,x){        if(!cz[(i-1)]&&!cz[(i-1)^1]){            cz[i-1]=1;            if(!bz[last[i]]){                tarjan(last[i]);                low[x]=min(low[x],low[last[i]]);            }            else if(az[last[i]])low[x]=min(low[x],dfn[last[i]]);        }    //  else if(az[last[i]])low[x]=min(low[x],dfn[last[i]]);    }    if(low[x]==dfn[x]){        hhh++;        while(stack[stack[0]+1]!=x){            az[stack[stack[0]]]=0;            a[stack[stack[0]]]=hhh;            stack[0]--;        }    }}int main(){    scanf("%d%d%d",&n,&m,&cas);    fo(i,1,m){        scanf("%d%d",&l,&t);b[i]=l,c[i]=t;        add(l,t),add(t,l);    }/*  if(n==m+1){        dfs1(1,0);        fo(j,1,20){            fo(i,1,n)f[i][j]=f[f[i][j-1]][j-1];        }        for(;cas;cas--){            scanf("%d%d",&k,&l);            int o=lca(k,l);            t=deep[k]+deep[l]-2*deep[o]+1;            ans+=t*(t-1);        }        printf("%lld\n",ans);    }    else{*/        fo(i,1,n){            if(!bz[i]){                tarjan(i);            }        }        fo(i,1,n){            size[a[i]]++;        }        fo(i,1,m){            if(a[b[i]]!=a[c[i]]){                add1(a[b[i]],a[c[i]]);                add1(a[c[i]],a[b[i]]);            }        }        tou=a[1];        dfs1(tou,0);        fo(j,1,20){            fo(i,1,hhh){                f[i][j]=f[f[i][j-1]][j-1];                g[i][j]=g[f[i][j-1]][j-1]+g[i][j-1];                p[i][j]=p[f[i][j-1]][j-1]+p[i][j-1];            }        }        for(;cas;cas--){            scanf("%d%d",&k,&l);            if(deep[a[k]]<deep[a[l]])swap(k,l);            if(a[k]!=a[l]){                int o=lca(a[k],a[l]);                zhi+=size[a[k]],zhi1+=(ll)size[a[k]]*size[a[k]];                if(o!=a[l])zhi+=size[a[l]],zhi1+=(ll)size[a[l]]*size[a[l]];                ans+=zhi*zhi-zhi1;            }        }        printf("%lld\n",ans);//  }}
2 0