【NOIP模拟】Map

Description

【NOIP模拟】Map_第1张图片

Solution

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

tarjan缩点

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

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

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

Code

#include
#include
#include
#include
#include
#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]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]]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);
//  }
}

你可能感兴趣的:(noip,树,倍增算法,tarjan)