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;
}