蓝书(算法竞赛进阶指南)刷题记录——BZOJ1123 BLO

版权声明:转载请注明原出处啦(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/82938175

题目:BZOJ1123.

题目大意:给定一张图,输出当与点i的相连的边都被去掉后,有多少个无需点对(x,y)不连通.

这道题其实不难.

首先我们知道,一个点i的所有边去掉后,这个点i肯定与其它点不相连了,所以不相连的对数先加上n-1.

然后我们发现这个点i若不是割点,那么对数就只有这么点.但是如果是割点,我们发现它的独立子树(即与点i的父亲不在一个联通块里的子树)与它的父亲所在的联通块之间会有对数.

我们设一棵独立子树的大小为size,则这个独立子树会产生的对数为(n-size)*size.

而我们设点i父亲所在连通块的大小为size,我们发现会产生的对数也为(n-size)*size.

其实蓝书上的公式跟这个公式是等价的.

而如何求一棵子树以j为根是不是独立子树?我们只要判断一棵子树是否有非树边连向点i的父亲所在连通块内的节点,即是否满足\bg_white low[j]<=dfn[i].

代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000,M=500000;
struct side{
  int y,next;
}e[M*2+9];
struct node{
  LL ans; 
  int dfn,low,size;
  bool cut;
}d[N+9];
int n,m,top=1,num,root=1,lin[N+9];
void ins(int X,int Y){
  e[++top].y=Y;
  e[top].next=lin[X];
  lin[X]=top;
}
void tarjan(int k){
  int flag=0,size=0;
  d[k].size=1LL;
  d[k].ans=LL(n-1);
  d[k].dfn=d[k].low=++num;
  for (int i=lin[k];i;i=e[i].next){
    int y=e[i].y;
    if (!d[y].dfn){
      tarjan(y);
      d[k].low=min(d[y].low,d[k].low);
      d[k].size+=d[y].size;
      if (d[y].low>=d[k].dfn){
        flag++;
        if (k^root||flag>1) d[k].cut=1;
        d[k].ans+=1LL*d[y].size*LL(n-d[y].size);
      }else size+=d[y].size;
    }else d[k].low=min(d[k].low,d[y].dfn);
  }
  size+=LL(n-d[k].size);
  d[k].ans+=1LL*size*LL(n-size);
}
Abigail into(){
  scanf("%d%d",&n,&m);
  int x,y;
  for (int i=1;i<=m;i++){
    scanf("%d%d",&x,&y);
    ins(x,y);ins(y,x);
  }
}
Abigail work(){
  tarjan(1);
}
Abigail outo(){
  for (int i=1;i<=n;i++)
    printf("%lld\n",d[i].ans);
}
int main(){
  into();
  work();
  outo();
  return 0;
}

m写成了n调了一个晚上...

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/82938175