Tarjan 算法做题记录

1|0T1-受欢迎的牛

题目链接

1|1Solution

裸题,Tarjan 缩点,找到那一个出度为 0 的点,把它所在的强连通分量的大小输出。如果有多个就无解。

1|2Code

这题是贺我们老师的,所以码风不像我的。

#include<bits/stdc++.h> using namespace std; #define maxn 11000 int t,n,m,size,du[maxn],line[maxn]; vector<int>edge[maxn]; vector<int>E[maxn]; int low[maxn],dfn[maxn] ,s[maxn],dfncnt,tp,sc,scc[maxn],sz[maxn]; bool in_stack[maxn]; void tarjan(int u) { low[u] = dfn[u] = ++dfncnt, s[++tp] = u, in_stack[u] = 1; for (int v:edge[u]) { if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (in_stack[v]) { low[u] = min(low[u], dfn[v]); } } if (dfn[u] == low[u]) { ++sc; do{ scc[s[tp]] = sc; sz[sc]++; in_stack[s[tp]] = 0; --tp; }while (s[tp+1] != u); //cout<<sz[sc]<<" "<<u<<endl; } } void work(){ for(int i=1;i<=n;i++)if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++) for(int y:edge[i])if(scc[i]!=scc[y]){ E[scc[i]].push_back(scc[y]); du[scc[i]]++; // cout<<du[scc[y]]<<" scc="<<scc[y]<<endl; } } int main(){ cin>>n>>m; for(int i=1,a,b;i<=m;i++){ cin>>a>>b; edge[a].push_back(b); } work(); int kk=0,t; for(int i=1;i<=sc;i++) if(du[i]==0)kk++,t=i; if(kk==1){ cout<<sz[t]<<endl; }else cout<<0<<endl; }

2|0T2-最大半连通子图

题目链接

2|1Solution

这个半连通子图和强连通分量的定义很像,可以发现,强连通分量必定是半连通子图,所以我们先 Tarjan 缩点,变成一个 DAG。然后再想,两个强连通分量通过一条有向边相连,必定是一个半连通子图。所以,问题就变成了再缩完点的 DAG 上寻找最长链。

然后进行拓扑排序,边排序边 DP,f(i) 表示到节点 i 的最长链大小,g(i) 表示到节点 i 最长链个数,要记得判重边。

2|2Code

#include<bits/stdc++.h> #define int long long using namespace std; void read(int &x) { char ch=getchar(); int r=0,w=1; while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar(); while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar(); x=r*w; } const int N=2e5+7; vector<int>edge[N]; vector<int>E[N]; int n,m,mod; int dfncnt,low[N],dfn[N],stk[N],scc[N],sc,top,sz[N],du[N]; bool in_stack[N]; map<pair<int,int>,bool>mp; queue<int>q; int f[N],g[N]; void tarjan(int u) { low[u]=dfn[u]=++dfncnt;stk[++top]=u;in_stack[u]=1; for(int v:edge[u]) { if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(in_stack[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { ++sc; do { scc[stk[top]]=sc; sz[sc]++; in_stack[stk[top]]=0; --top; }while(stk[top+1]!=u); } } void work() { for(int u=1;u<=n;u++) for(int v:edge[u]) if(scc[u]!=scc[v]&&!mp[make_pair(scc[u],scc[v])]) E[scc[u]].push_back(scc[v]),mp[make_pair(scc[u],scc[v])]=1,du[scc[v]]++; } void topsort() { for(int i=1;i<=sc;i++) if(!du[i]) { q.push(i); f[i]=sz[i]; g[i]=1; } while(!q.empty()) { int u=q.front();q.pop(); for(int v:E[u]) { du[v]--; if(!du[v])q.push(v); if(f[u]+sz[v]==f[v]) { g[v]+=g[u]; g[v]%=mod; } else if(f[u]+sz[v]>f[v]) { g[v]=g[u]; f[v]=f[u]+sz[v]; } } } int ans=0,ans1=0; for(int i=1;i<=sc;i++) { if(f[ans]<f[i]) { ans=i; ans1=g[i]; } else if(f[ans]==f[i]) ans1+=g[i]; ans1%=mod; } printf("%lld\n%lld",f[ans],ans1); } main() { read(n);read(m);read(mod); for(int i=1,x,y;i<=m;i++) read(x),read(y),edge[x].push_back(y); for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i); work();topsort(); return 0; }

3|0T3-网络协议

题目链接

3|1Solution

第一问很好解决,缩点后寻找入度为 0 的点即可。

对于第二问,要求加边,使缩点后的每个点都能到另一个点,即每个点都需要有入度和出度都大于 1,即我们寻找入度为 0 的点和出度为 0 的点,取最大值即可。

3|2Code

#include<bits/stdc++.h> #define int long long using namespace std; void read(int &x) { char ch=getchar(); int r=0,mp=1; while(!isdigit(ch))mp=ch=='-'?-1:1,ch=getchar(); while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar(); x=r*mp; } const int N=1e4+7; int n,m,k,dfncnt,size,top,scc; int dfn[N],low[N],stk[N],sc[N],sz[N],du[N],f[N]; bool in_stack[N]; map<pair<int,int>,bool>mp; vector<int>edge[N]; vector<int>Edge[N]; queue<int>q; void tarjan(int u) { dfn[u]=low[u]=++dfncnt; stk[++top]=u; in_stack[u]=1; for(int v:edge[u]) { if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else { if(in_stack[v]) low[u]=min(low[u],dfn[v]); } } if(dfn[u]==low[u]) { scc++; do { sc[stk[top]]=scc; sz[scc]++; in_stack[stk[top]]=0; top--; } while(stk[top+1]!=u); } } main() { read(n); for(int i=1;i<=n;i++) { int p; read(p); while(p!=0) { edge[i].push_back(p); read(p); } } for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i); for(int u=1;u<=n;u++) { for(int v:edge[u]) if(sc[u]!=sc[v]&&mp[make_pair(sc[u],sc[v])]!=1) { Edge[sc[u]].push_back(sc[v]); mp[make_pair(sc[u],sc[v])]=1; du[sc[v]]++; f[sc[u]]++; } } int ans=0,anss=0; for(int i=1;i<=scc;i++) { if(!du[i])ans++; if(!f[i])anss++; } printf("%lld",ans); if(scc==1)printf("\n%lld",0); else printf("\n%lld",max(ans,anss)); return 0; }

4|0T4-消息的传递

题目链接

4|1Solution

跟上一题一样的,还不要第二问。

5|0T5-间谍网络

题目链接

5|1Solution

这一题跟前几题不同,跑 Tarjan 的条件要多一个可以被收买,因为在一个强连通分量中必须要有一个可以被收买的才能够全部逮捕。
缩点的时候记录一下最少的收买值。统计答案时如果有一个点的时间戳没有被更新过,那就代表他既不能被收买,也不能被别人告发,那就是不可控制。
找到所有入度为 0 的点,加上它的收买值就好了。

5|2Code

#include<bits/stdc++.h> #define int long long using namespace std; void read(int &x) { char ch=getchar(); int r=0,w=1; while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar(); while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar(); x=r*w; } const int N=2e5+7; vector<int>edge[N]; vector<int>E[N]; int n,m,mod,a[N],sum[N]; int dfncnt,low[N],dfn[N],stk[N],scc[N],sc,top,sz[N],du[N]; bool in_stack[N]; map<pair<int,int>,bool>mp; queue<int>q; int f[N],g[N]; void tarjan(int u) { low[u]=dfn[u]=++dfncnt;stk[++top]=u;in_stack[u]=1; for(int v:edge[u]) { if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(in_stack[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { ++sc; do { scc[stk[top]]=sc; sz[sc]++; in_stack[stk[top]]=0; sum[sc]=min(sum[sc],a[stk[top]]); --top; }while(stk[top+1]!=u); } } void work() { for(int i=1;i<=n;i++) { if(!dfn[i]) { printf("NO\n"); printf("%d\n",i); return; } } for(int u=1;u<=n;u++) for(int v:edge[u]) if(scc[u]!=scc[v]&&!mp[make_pair(scc[u],scc[v])]) E[scc[u]].push_back(scc[v]),mp[make_pair(scc[u],scc[v])]=1,du[scc[v]]++; printf("YES\n"); int ans=0; for(int i=1;i<=sc;i++) if(!du[i])ans+=sum[i]; printf("%d",ans); } main() { int p; read(n);read(p);//read(m); memset(a,63,sizeof a); memset(sum,63,sizeof sum); for(int i=1,x;i<=p;i++) read(x),read(a[x]); read(m); for(int i=1,x,y;i<=m;i++) read(x),read(y),edge[x].push_back(y); for(int i=1;i<=n;i++) if(!dfn[i]&&a[i]<1e9)tarjan(i); work(); return 0; }

6|0T6-抢掠计划

题目链接

6|1Solution

既然可以重复走,那我们先缩点,每个点的点权定义为这个强连通分量里面 ATM 的和,然后把点权移到边权,跑最长路,最后再枚举每一个酒吧,取最长路的最大值。

6|2Code

#include<bits/stdc++.h> #define int long long using namespace std; void read(int &x) { char ch=getchar(); int r=0,mp=1; while(!isdigit(ch))mp=ch=='-'?-1:1,ch=getchar(); while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar(); x=r*mp; } const int N=1e6+7; int n,m,k,dfncnt,size,top,scc; int dfn[N],low[N],stk[N],sc[N],sum[N],du[N],f[N],my[N],sb[N]; int d[N],pre[N],dis[N],son[N],now[N],tot; bool in_stack[N],bb[N]; map<pair<int,int>,bool>mp; vector<int>edge[N]; void add(int x,int y,int z) { pre[++tot]=now[x]; son[tot]=y; dis[tot]=z; now[x]=tot; } queue<int>q; void tarjan(int u) { dfn[u]=low[u]=++dfncnt; stk[++top]=u; in_stack[u]=1; for(int v:edge[u]) { if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else { if(in_stack[v]) low[u]=min(low[u],dfn[v]); } } if(dfn[u]==low[u]) { scc++; do { sc[stk[top]]=scc; sum[scc]+=my[stk[top]]; in_stack[stk[top]]=0; top--; } while(stk[top+1]!=u); } } void spfa(int s) { queue<int>q; memset(d,128,sizeof d); memset(bb,false,sizeof bb); d[s]=sum[s];q.push(s);bb[s]=1; while(!q.empty()) { int u=q.front();q.pop(); bb[u]=0; for(int i=now[u];i;i=pre[i]) { int v=son[i],w=dis[i]; if(d[v]<d[u]+w) { d[v]=d[u]+w; if(!bb[v])q.push(v),bb[v]=1; } } } } main() { read(n);read(m); for(int i=1,x,y;i<=m;i++) { read(x);read(y); edge[x].push_back(y); } for(int i=1;i<=n;i++) read(my[i]); for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i); int s,p; read(s);read(p); for(int i=1;i<=p;i++) read(sb[i]); for(int u=1;u<=n;u++) { for(int v:edge[u]) if(sc[u]!=sc[v]&&mp[make_pair(sc[u],sc[v])]!=1) { add(sc[u],sc[v],sum[sc[v]]); mp[make_pair(sc[u],sc[v])]=1; } } spfa(sc[s]); int ans=0; for(int i=1;i<=p;i++) ans=max(ans,d[sc[sb[i]]]); cout<<ans; return 0; }

__EOF__

本文作者JMartin
本文链接https://www.cnblogs.com/LAK666/p/16559662.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Epoch_L  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示