洛谷P3469 [POI2008]BLO-Blockade (Tarjan判割点+dfs树切割)
题目链接:https://www.luogu.com.cn/problem/P3469
题目解法:
错解(50pts):我先用Tarjan跑了一把找到所有割点,因为非割点的话造成的影响肯定是2*(n-1),所以我们要特殊处理割点的情况。于是呢,我就把割点的相临点全部跑了一把bfs找该组有几个点,然后总点数带来的贡献-各个小组点数带来的贡献和就是最后答案。但是这个复杂度爆了TLE,所以只有50pts
AC解法(100pts):利用tarjan的同时建立dfs树,因为low[v]>=dfn[x]就能判断x是不是割点。在dfs树上会有一些假子节点。而通过刚刚那个判断,我们可以把每一个真子结点进行处理,if(low[v]>=dfn[x]) siz[x]+=siz[v],这个情况就是真子节点因为low[v]和dfn[x]之间 >的情况是是一个单链,而=的情况是一个环。建成真的dfs树后,后面的处理就容易想了。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define endl '\n' #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=5e5+5; int tot,head[maxn]; struct E{ int to,next; }edge[maxn<<1]; void add(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } int n,m; int id[maxn],dfn[maxn],low[maxn],tott,cnt; stack<int> s;int fa[maxn],cut[maxn]; vector<int> bccs[maxn];int siz[maxn];ll ans[maxn]; void tarjan(int x){ low[x]=dfn[x]=++tott; s.push(x);int son=0;siz[x]=1;ll sum=0; for(int i=head[x];i!=-1;i=edge[i].next){ int v=edge[i].to; if(!dfn[v]){ ++son;fa[v]=x; tarjan(v);siz[x]+=siz[v]; low[x]=min(low[x],low[v]); if(low[v]>=dfn[x]){ cut[x]=true; ans[x]+=1LL*siz[v]*(n-1-siz[v]); sum+=siz[v]; ++cnt; bccs[cnt].clear(); while(1){ int now=s.top();s.pop(); id[now]=cnt; bccs[cnt].push_back(now); if(now==v) break; } id[x]=cnt; bccs[cnt].push_back(x); } } else if(v!=fa[x]) low[x]=min(low[x],dfn[v]); } if(fa[x]==-1&&son==1) cut[x]=false; if(!cut[x]) ans[x]=2*(n-1); else{ ans[x]+=1LL*(n-sum-1)*(sum)+2*(n-1); } } int main(){ cin>>n>>m;mem(head,-1); rep(i,1,m){ int u,v;cin>>u>>v; add(u,v);add(v,u); } rep(i,1,n){ if(!dfn[i]){fa[i]=-1;tarjan(i);} } rep(i,1,n){ cout<<"TEST "<<dfn[i]<<" "<<low[i]<<endl; } }
前ICPC算法竞赛退役选手|现摸鱼ing