题目链接:https://www.luogu.org/problemnew/show/P3469
题意:在n个点m条边的无向图,求删掉一个点后,有多少个有序(x,y)由连通到不连通
思路:
分两种情况:
1.点不为割点,答案就是(n-1)*2(这个点到其他的点,其他的点到这个点)
2.点是割点,那么图被分成一些部分,答案就是每个部分点的个数与其他部分的个数乘积的和,再加上(n-1)*2(即上面的)
举个例子:
比如删掉割点3时
每个部分与其他点的乘积的和:12,也可以这样计算:
集合(1) * 集合(2) =1
集合(1,2) * 集合(4)=2
集合(1,2,4)* 集合(5)=3 这里只算了一边,所以答案就是 6*2
怎么实现?在搜索中加上去就行,看代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; const int maxn=1e5+10; struct node{ int next,to; }edge[maxn*10]; ll head[maxn],low[maxn],dfn[maxn]; ll ans[maxn],size[maxn];//ans为答案,size[i]存储以i为根子树的大小 int n,m,cnt,tot; void add(int u,int v) { edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt++; } void tarjan(int u) { low[u]=dfn[u]=++tot; ll z=0;//记录已求出割点后集合的大小,相当于上面列子左边的集合 size[u]=1;//初始 for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(!dfn[v]) { tarjan(v); size[u]+=size[v];//没遍历过当然要加上子树的大小,来求出以u为根子树的大小 low[u]=min(low[u],low[v]); if(low[v]>=dfn[u])//v为割点 { ans[u]+=(ll)z*size[v];//模拟上面例子乘的过程 z+=size[v];//记得集合变大 } } else low[u]=min(low[u],dfn[v]); } ans[u]+=(ll)z*(n-1-z);//不要忘了,这是最后一步(不知道具体用处,个人理解是算不是割点时的答案) } int main() { cnt=tot=0; memset(head,-1,sizeof(head)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(ans,0,sizeof(ans)); memset(size,0,sizeof(size)); int x,y; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } tarjan(1); for(int i=1;i<=n;i++) printf("%lld\n",(ans[i]+n-1)<<1); return 0; }