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;
} 
posted on 2024-03-02 16:44  _XOFqwq  阅读(1)  评论(0编辑  收藏  举报