Living-Dream 系列笔记 第47期

Posted on 2024-03-02 16:44  _XOFqwq  阅读(2)  评论(0编辑  收藏  举报

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