3896. 【NOIP2014模拟10.26】战争游戏 (Standard IO)
Time Limits: 1000 ms Memory Limits: 262144 KB
题目大意
n个点,m条无向边连成一个连通图。求对于任一点i,若把i删除,有多少点对无法互相到达。
Input
第一行n,m
2....m+1行,每行u v,表示u,v有一条边相连
Output
n行,第i行表示若将i删除,有多少点对不连通(包括i)
Sample Input
7 9
1 2
1 3
1 4
1 5
1 6
1 7
2 3
4 5
6 7
Sample Output
18
6
6
6
6
6
6
Data Constraint
n<=50000;m<=100000
解法::
tarjan+树形dp
缩点双联通分量,然后在树上计算。
实际上可以在 tarjn 的时候顺便处理,要注意 i 是割点不代表删了 i, 的子孙跟父亲都
断了(具体见 game1.cp)。
考虑点与点之间必须经过的只有割点,如果在同一个点双联通分量中的话,则没有必
经的点,那么我们只需要把所有的点双联通分量缩点,之后形成一棵树的结构,在树上计算
经过每个点的点对数量,dp解决。
让我介绍下tarjan求割点吧
选任一点dfs,设dfn[i]为点i的遍历序号
low[i]为不通过i的父节点可以追溯到的序号最小的节点。
显然low[i]=min(dfn[i],low[j]) && j!=i_father&&connect[i][j]=1
对于点u,v
if(dfn[u]<=low[v])
u为割点。
dp很简单详见程序
#include
#include
#include
#include
using namespace std;
const int N = 50010 , M=2*N;
struct edge{
int y,l;
}f[M*2];
int ans[N],siz[N],i,j,k,g[N],T,dfn[N],ind,low[N],m,n;
void ins(int a,int b){
f[++T].y=b;
f[T].l=g[a];
g[a]=T;
}
bool v[N];
void dfs(int p,int fa){
int i,j,k,L=0;
++ind;
low[p]=dfn[p]=ind;
for(k=g[p];k;k=f[k].l){
v[f[k].y]=0;
if(dfn[f[k].y]==0){
dfs(f[k].y,p);
siz[p]+=siz[f[k].y];
if(dfn[p]<=low[f[k].y]){
L+=siz[f[k].y];
}
v[f[k].y]=1;
}
if(f[k].y!=fa)low[p]=min(low[f[k].y],low[p]);
}
int A=0,B=0;
for(k=g[p];k;k=f[k].l){
if(dfn[p]<=low[f[k].y]&&v[f[k].y]){
A+=(L-siz[f[k].y])*siz[f[k].y];
B+=(n-L-1)*siz[f[k].y];
}
}
A/=2;
ans[p]=A+B+n-1;
}
int main(){
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
ins(a,b);ins(b,a);
}
ind=0;
for(i=1;i<=n;i++)siz[i]=1;
dfs(1,0);
for(i=1;i<=n;i++)printf("%d\n",ans[i]);
}