tarjan算法
tarjan算法
记1月9日至1月17日,tarjan算法暂时告一段落。
个人体会:
主要功能其一:求解连通分量,将图转化成树。树的特性很多,利用树的特性解题。
主要功能其二:求出并理解桥和割点,掌握生成和消除桥或割点的方法,算答案或算贡献。
算法能用的东西不多,但写题的过程还是很欢乐的,代码不长不短,解题要些思维。
放上几个板子:
tarjan求强连通分量(缩点)
#include<iostream>
#include<stack>
#include<vector>
#include<cstring>
using namespace std;
#define ll long long
ll n,m,a[10007],u,v;
ll dfn[10007],low[10007],vis[10007],sum[10007],pos[10007],cnt;
ll dp[10007];
stack<ll>sa;
vector<ll>ho[10007];
vector<ll>aq[10007];
void dfs(ll p){
dfn[p]=low[p]=++cnt;
sa.push(p);
vis[p]=1;
for(int i=0;i<ho[p].size();i++){
ll to=ho[p][i];
if(dfn[to]==0){
dfs(to);
low[p]=min(low[p],low[to]);
}
else if(vis[to]==1){
low[p]=min(low[p],dfn[to]);
}
}
if(low[p]==dfn[p]){
while(!sa.empty()){
ll now=sa.top();
sa.pop();
vis[now]=0;
aq[p].push_back(now);
sum[p]+=a[now];
pos[now]=p;
if(now==p){
break;
}
}
}
}
ll fin(ll p){
ll res=0;
if(dp[p]!=-1){
return dp[p];
}
for(int i=0;i<aq[p].size();i++){
ll now=aq[p][i];
for(int j=0;j<ho[now].size();j++){
ll to=pos[ho[now][j]];
if(pos[to]!=pos[p]){
res=max(res,fin(to));
}
}
}
return dp[p]=res+sum[p];
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=m;i++){
scanf("%lld%lld",&u,&v);
ho[u].push_back(v);
}
for(int i=1;i<=n;i++){
if(dfn[i]==0){
dfs(i);
}
}
ll ans=0;
memset(dp,-1,sizeof(dp));
for(int i=1;i<=n;i++){
ans=max(ans,fin(pos[i]));
}
printf("%lld\n",ans);
}
tarjan求桥
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
ll n,m,u,v;
ll dfn[100007],low[100007],cnt;
vector<ll>ho[100007];
vector<pair<ll,ll> >ans;
void tarjan(ll p,ll f){
dfn[p]=low[p]=++cnt;
for(int i=0;i<ho[p].size();i++){
ll to=ho[p][i];
if(to==f)continue;
if(dfn[to]==0){
tarjan(to,p);
low[p]=min(low[p],low[to]);
if(low[to]>dfn[p]){
ans.push_back({min(to,p),max(p,to)});
}
}
else{
low[p]=min(low[p],dfn[to]);
}
}
}
void init(){
ans.clear();
cnt=0;
for(int i=0;i<n;i++){
ho[i].clear();
dfn[i]=low[i]=0;
}
}
int main(){
while(~scanf("%lld",&n)){
init();
for(int i=1;i<=n;i++){
char ch;
scanf("%lld (%lld)",&u,&m);
while(m--){
scanf(" %lld",&v);
if(u>=v)continue;
ho[u].push_back(v);
ho[v].push_back(u);
}
}
for(int i=0;i<n;i++){
if(dfn[i]==0){
tarjan(i,-1);
}
}
sort(ans.begin(),ans.end());
printf("%d critical links\n",ans.size());
for(int i=0;i<ans.size();i++){
printf("%lld - %lld\n",ans[i].first,ans[i].second);
}puts("");
}
return 0;
}
tarjan求割点
#include<iostream>
#include<vector>
#include<set>
using namespace std;
#define ll long long
ll n,u,v;
ll dfn[107],low[107],cnt;
vector<ll>ho[107];
set<ll>sa;
void tarjan(ll p,ll k){
ll tot=0;
dfn[p]=low[p]=++cnt;
for(int i=0;i<ho[p].size();i++){
ll to=ho[p][i];
if(dfn[to]==0){
tot++;
tarjan(to,0);
low[p]=min(low[p],low[to]);
if(low[to]>=dfn[p]&&k==0){
sa.insert(p);
}
}
else{
low[p]=min(low[p],dfn[to]);
}
}
if(k==1&&tot>=2){
sa.insert(p);
}
}
int main(){
while(~scanf("%lld",&n)){
if(n==0)break;
sa.clear();
for(int i=1;i<=n;i++){
ho[i].clear();
dfn[i]=0;
low[i]=0;
cnt=0;
}
while(1){
scanf("%lld",&u);
if(u==0)break;
while(1){
scanf("%lld",&v);
ho[u].push_back(v);
ho[v].push_back(u);
if(getchar()=='\n'){
break;
}
}
}
for(int i=1;i<=n;i++){
if(dfn[i]==0){
tarjan(i,1);
}
}
printf("%lld\n",sa.size());
}
}
tarjan求双连通分量
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
#define ll long long
ll n,m,u,v,a[500007];
ll dfn[500007],low[500007],pos[500007],ans[500007],cnt;
vector<ll>ho[500007];
vector<ll>aq[500007];
vector<ll>ans2;
stack<ll>sa;
void tarjan(ll p,ll f){
dfn[p]=low[p]=++cnt;
sa.push(p);
for(int i=0;i<ho[p].size();i++){
ll to=ho[p][i];
if(to==f)continue;
if(dfn[to]==0){
tarjan(to,p);
low[p]=min(low[p],low[to]);
}
else{
low[p]=min(low[p],dfn[to]);
}
}
if(low[p]==dfn[p]){
while(!sa.empty()){
ll lin=sa.top();
sa.pop();
pos[lin]=p;
ans[p]^=a[lin];//一个双连通分量的权值异或和
aq[p].push_back(lin);
if(lin==p){
break;
}
}
}
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=m;i++){
scanf("%lld%lld",&u,&v);
ho[u].push_back(v);
ho[v].push_back(u);
}
for(int i=1;i<=n;i++){
if(dfn[i]==0){
tarjan(i,0);
}
}
for(int i=1;i<=n;i++){
if(pos[i]!=i)continue;
else{
ans2.push_back(ans[i]);
}
}
sort(ans2.begin(), ans2.end());
for(int i=0;i<ans2.size();i++){
printf("%lld\n",ans2[i]);
}
}