Living-Dream 系列笔记 第47期
T1
第一问是简单的,答案即为缩点后入度为 \(0\) 的点。
对于第二问,考虑到当整个图就是一个 SCC 时能满足条件。
当整个图为一个 SCC 时,它仅可能是一个环。
而我们知道,让一个 DAG 形成环肯定要
将出度为 \(0\) 的点全部与入度为 \(0\) 的点相连。
于是第二问的答案即为 \(\max(in,out)\),
其中 \(in\) 表示入度为 \(0\) 的点的个数,\(out\) 为出度为 \(0\) 的点的个数。
#include<bits/stdc++.h> using namespace std; const int N=1e2+5,M=N*N; int n,m; int ans1,ans2; int p,cnt,tot; bool instk[N]; stack<int> s; int dfn[N],low[N],scc[N]; int in[N],out[N]; vector<int> G[M]; struct Edge{ int u,v; }e[M]; void tarjan(int u){ s.push(u),instk[u]=1,dfn[u]=low[u]=++cnt; for(int i:G[u]){ if(!dfn[i]) tarjan(i),low[u]=min(low[u],low[i]); else if(instk[i]) low[u]=min(low[u],dfn[i]); } if(dfn[u]==low[u]){ ++tot; for(;s.top()!=u;s.pop()) instk[s.top()]=0,scc[s.top()]=tot; instk[u]=0,scc[u]=tot,s.pop(); } } int main(){ cin>>n; for(int i=1,v;i<=n;i++) while(cin>>v&&v) G[i].push_back(v),e[++p]={i,v}; for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=p;i++){ int x=scc[e[i].u],y=scc[e[i].v]; if(x!=y) in[y]++,out[x]++; } for(int i=1;i<=tot;i++){ if(!in[i]) ans1++; if(!out[i]) ans2++; } cout<<ans1<<'\n'<<(tot==1?0:max(ans1,ans2)); return 0; }
作业 T1
好题。
这题一眼 toposort + dp。于是我们先上个 tarjan 缩点。
考虑如何建边(我们默认从小往大建边):
-
对于 \(X=1\),显然建出 \(A,B\) 双向边权为 \(0\) 的边即可。
-
对于 \(X=2\),考虑到要求答案最小化,因此建出 \(A \to B\) 边权为 \(1\) 的边即可。
-
对于 \(X=3\),考虑到要求答案最小化,因此建出 \(B \to A\) 边权为 \(0\) 的边即可。
-
对于 \(X=4\),考虑到要求答案最小化,因此建出 \(B \to A\) 边权为 \(1\) 的边即可。
-
对于 \(X=5\),考虑到要求答案最小化,因此建出 \(A \to B\) 边权为 \(0\) 的边即可。
然后愉快地进行 toposort + dp 即可。
转移:\(dp_{nxt}=\max(dp_{nxt},dp_{cur}+w)\)(\(w\) 为 \(cur \to nxt\) 的边权)。
答案:\(\sum_{i=1}^{tot} dp_i \times sum_i\)(\(tot\) 为 SCC 数量,\(sum\) 为 SCC 的大小)。
关于 \(-1\) 情况的判定,我们重建图时若枚举到 SCC(即一个环)中一条边的边权 \(\neq 0\),则说明产生了矛盾。
#include<bits/stdc++.h> #define int long long using namespace std; const int N=5e5+5,M=5e5+5; int n,m,ans; int p,tot,cnt; bool instk[N]; stack<int> s; int dfn[N],low[N],scc[N],sum[N]; int dp[N],in[N]; struct edge{ int u,v,w; }e[M]; struct Edge{ int v,w; }; vector<Edge> G[M],V[M]; void tarjan(int u){ s.push(u),instk[u]=1,dfn[u]=low[u]=++cnt; for(auto i:G[u]){ if(!dfn[i.v]) tarjan(i.v),low[u]=min(low[u],low[i.v]); else if(instk[i.v]) low[u]=min(low[u],dfn[i.v]); } if(dfn[u]==low[u]){ ++tot; for(;s.top()!=u;s.pop()) instk[s.top()]=0,scc[s.top()]=tot,sum[tot]++; instk[u]=0,scc[u]=tot,sum[tot]++,s.pop(); } } void topo(){ queue<int> q; for(int i=1;i<=tot;i++) if(!in[i]) q.push(i),dp[i]=1; while(!q.empty()){ int now=q.front(); q.pop(); for(auto i:V[now]){ --in[i.v],dp[i.v]=max(dp[i.v],dp[now]+i.w); if(!in[i.v]) q.push(i.v); } } } signed main(){ cin>>n>>m; for(int i=1,op,u,v;i<=m;i++){ cin>>op>>u>>v; if(op==1) G[u].push_back({v,0}),G[v].push_back({u,0}),e[++p]={u,v,0},e[++p]={v,u,0}; else if(op==2) G[u].push_back({v,1}),e[++p]={u,v,1}; else if(op==3) G[v].push_back({u,0}),e[++p]={v,u,0}; else if(op==4) G[v].push_back({u,1}),e[++p]={v,u,1}; else G[u].push_back({v,0}),e[++p]={u,v,0}; } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=p;i++){ int x=scc[e[i].u],y=scc[e[i].v]; if(x==y&&e[i].w==1) cout<<-1,exit(0); if(x!=y) V[x].push_back({y,e[i].w}),in[y]++; } topo(); for(int i=1;i<=tot;i++) ans+=dp[i]*sum[i]; cout<<ans; return 0; }
比赛链接
T1
\(100\)。
#include<bits/stdc++.h> using namespace std; const int N=1e3+5,M=1e3+5; int n,m,ans=-1e9; int dis[N]; bool vis[N]; struct Edge{ int v,c,f; }; vector<Edge> G[M]; void spfa(int s,int x){ memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); queue<int> q; q.push(s),dis[s]=0,vis[s]=1; while(!q.empty()){ int now=q.front(); q.pop(),vis[now]=0; for(auto i:G[now]){ if(dis[i.v]>dis[now]+i.c&&i.f>=x){ dis[i.v]=dis[now]+i.c; if(!vis[i.v]) q.push(i.v),vis[i.v]=1; } } } } int main(){ //freopen("T1.in","r",stdin); //freopen("T1.out","w",stdout); cin>>n>>m; for(int i=1,u,v,c,f;i<=m;i++) cin>>u>>v>>c>>f, G[u].push_back({v,c,f}), G[v].push_back({u,c,f}); for(int i=1;i<=1000;i++){ spfa(1,i); ans=max(ans,1000000*i/dis[n]); } cout<<ans; return 0; }
T2
\(100 \to 80\)。
错因:传参数组不可直接 sizeof
取首地址。具体做法见 here。
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e3+5,M=1e4+5; int n,m; int dis1[N],disn[N],cnt[N]; bool vis[N]; struct Edge{ int v,w; }; vector<Edge> G[M]; bool spfa(int s,int dis[]){ memset(vis,0,sizeof(vis)); memset(dis,0x3f,N*sizeof(long long)); //错误原因 memset(cnt,0,sizeof(cnt)); queue<int> q; q.push(s),dis[s]=0,vis[s]=1; while(!q.empty()){ int now=q.front(); q.pop(),vis[now]=0; for(auto i:G[now]){ if(dis[i.v]>dis[now]+i.w){ dis[i.v]=dis[now]+i.w; cnt[i.v]=cnt[now]+1; if(cnt[i.v]>n-1) return 1; if(!vis[i.v]) q.push(i.v),vis[i.v]=1; } } } return 0; } signed main(){ //freopen("T2.in","r",stdin); //freopen("T2.out","w",stdout); cin>>n>>m; for(int i=1,u,v,w;i<=m;i++) { cin>>u>>v>>w; G[u].push_back({v,-w}); } if(spfa(1,dis1)||spfa(n,disn)) cout<<"Forever love"; else cout<<min(dis1[n],disn[1]); return 0; }
T3
\(100 \to 0\)。
错因:没建双向边。
#include<bits/stdc++.h> using namespace std; const int N=2e3+5,M=1e5+5; int n,m,a,b; double dis[N]; bool vis[N]; struct Edge{ int v; double w; }; vector<Edge> G[M]; bool spfa(int s){ for(int i=1;i<=n;i++) dis[i]=1e9; memset(vis,0,sizeof(vis)); queue<int> q; q.push(s),dis[s]=100.0,vis[s]=1; while(!q.empty()){ int now=q.front(); q.pop(),vis[now]=0; for(auto i:G[now]){ if(dis[i.v]>dis[now]/i.w){ dis[i.v]=dis[now]/i.w; if(!vis[i.v]) q.push(i.v),vis[i.v]=1; } } } return 0; } int main(){ //freopen("T3.in","r",stdin); //freopen("T3.out","w",stdout); cin>>n>>m; for(int i=1,u,v,w;i<=m;i++) cin>>u>>v>>w, G[v].push_back({u,(1-0.01*w)}),G[u].push_back({v,(1-0.01*w)}); //错误原因 cin>>a>>b; spfa(b); cout<<setprecision(8)<<fixed<<dis[a]; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具