题目
分析:
如果一个点不是割点,那么将其剪去后,只会产生它自己到其他n-1个点的2*(n-1)个有序点对。
如果一个点是割点,将其剪去后,贡献来源于:
1.它子树中两两产生的点对
2.它所有子树与除了它子树之外的点产生的点对
3.它自己和n-1个点产生的点对
(这里的子树都指的是tarjan遍历出来的搜索树中的子树)
注意不要算重!!
最后说明一下割点的判定法则:
一个点是割点只需要子树中有至少1个点满足:
low[ v ] >=dfn[ u ] 即这个点不能到达比u更高的点,所以当u删去后这个点一定会被分裂开,满足割点的定义。
而如果一个点是根节点,还要满足:
有至少2个点满足上述条件:只有这样,断去根才会产生不同的块。
#include<bits/stdc++.h> using namespace std; #define ll long long #define ri register int #define N 100005 #define M 500005 int dfn[N],siz[N],low[N],tot=0,head[N],nex[M<<1],to[M<<1],cut[N],n,m,Ti=0; ll ans[N]; void add(int a,int b) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; } void tarjan(int x) { dfn[x]=low[x]=++Ti; siz[x]=1;//!!记得初始化 int fl=0,sum=0; for(ri i=head[x];i;i=nex[i]){ int v=to[i]; if(!dfn[v]){ tarjan(v); siz[x]+=siz[v];//类dfs 在搜索完了之后累计siz值 low[x]=min(low[x],low[v]); if(low[v]>=dfn[x]){ ans[x]+=(ll) siz[v]*(n-siz[v]);//统计对于x的某一颗子树割掉后 对剩下点产生的影响 fl++; sum+=siz[v]; if(x!=1 || fl>1) cut[x]=true; //割点判断法则:如果一个点是根节点 当且仅当搜索树上存在至少两个点满足上述条件 } } else low[x]=min(low[x],dfn[v]); } if(cut[x]) ans[x]+=(ll)(n-sum-1)*(sum+1) + (n-1);//统计剩下点与x的所有子树产生的点对 //为什么只加一个n-1呢 是因为之前求子树对其他点产生的点对时已经加过了 else ans[x]=2*(n-1);//点对是有序的 所以要*2 } int main() { int a,b; scanf("%d%d",&n,&m); for(ri i=1;i<=m;++i) scanf("%d%d",&a,&b),add(a,b),add(b,a); tarjan(1); for(ri i=1;i<=n;++i) printf("%lld\n",ans[i]); } /* 5 5 1 2 2 3 1 3 3 4 4 5 */