Loading

5.1考试题解

5.1考试题解

0501图论测 - 题单

现在是大图论时代!!!!

T1 Travel

数据如上

我们发现,根据题面描述,每个节点有且仅有一条出边.此时就会出现如下唯三种情况

  • 形成一个环

  • 形成一条链

  • 形成一条最终指向一个环的链

同时有了如下性质

  • 一个在环内的节点最大能得到的总值其实就是这个环所有点权之和(很好证明,因为每个节点只会有一条出边,所以如果有一个节点指向了环外一个节点,则不存在此环)
  • 一个在链上的节点最大能得到的总值就是走到底的点权的总和(容易发现如果走到环上一点就不必继续走了,可以直接不继续累加下去了,此时可以直接加上这个环内的点权之和)

此时就有了想法

找环
求环内总值
对于环外点走到底然后赋值

对于找环,很容易想到Top Sort对吧(O(n+m)的时间复杂度还真是让人喜爱(雾))

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;
const int INF=1e4;
int n,A[N],in[N];
int to[N];
int vis[N],val[N]; 
void topsort(){
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(in[i] == 0){q.push(i);vis[i]=2;}
	}
	while(q.size()){
		int t = q.front(),v=to[t];q.pop();in[v]--;
		if(in[v]==0){vis[v] = 2;q.push(v);}
	}
}//拓扑排序 
int dfs(int now,int sum){
	int v=to[now];
	if(vis[v]==1)
		return val[now]=val[now]+sum;
	vis[now] = 1;
	val[now] = dfs(v,sum+val[now]);
	return val[now];
}//dfs对于环内的所有节点进行赋值 
void dfs0(int now){
	int v=to[now];
	if(vis[v] == 1){
		val[now] = val[v]+A[now];return;
	}
	dfs0(v);
	val[now] = val[v]+A[now];
}//dfs对于所有非环内节点进行赋值 
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>A[i];
	}
	for(int u=1;u<=n;u++){
		int v;cin>>v;
		to[u]=v;
		in[v]++;
	}
	topsort(); 
	//存图,拓扑 
	for(int i=1;i<=n;i++)val[i]=A[i];
	for(int i=1;i<=n;i++){
		if(vis[i]!=2&&vis[i]!=1){
			vis[i]=1;
			dfs(i,0);
		}
	}//找到环上节点,赋值 
	for(int i=1;i<=n;i++){
		if(vis[i] == 2){
			vis[i] = 1;
			dfs0(i);
		}
	}//找到环外节点,标记为环上节点,赋值 
	for(int i=1;i<=n;i++)cout<<val[i]<<"\n";
	return 0;
} 

(挺恶心的,调了半天)

T2 Sns

我们发现,对于一个图中,如果点对(u,v,w)不在同一连通块,则一定不可能连接;在同一连通块则一定可以连接。所以我们想到可以找一个连通块,统计连通块中有几个节点,然后计算可以选择多少组点对(排列组合Right?),最后减去m就好了。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;
int n,m;
int fa[N];
int size[N];
int find(int x){
	return fa[x] == x?x:fa[x] = find(fa[x]);
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		fa[i]=i,size[i] = 1;
	}
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		int a=find(x),b=find(y);
		if(a!=b)
			fa[a] = b,size[b]+=size[a];
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(fa[i] == i){
			ans+=size[i]*(size[i]-1)/2;
		}
	}
	ans-=m;
	cout<<ans;
	return 0;
} 

T3 Junction

数据1如上图

我们发现,一条同时进过a,b的路径,左端点一定在a左侧,右端点一定在b右侧(就图而言)

假设a左侧有al个节点,b右侧有br个节点,则ans一定等于\(al\times br\)(排列组合Right?)

最后,对于al,br可以通过dfs隔离的方法求得

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int n,m,a,b;
int sum;
int vis[N];
vector<int>g[N];
void dfs(int now){
	for(auto i:g[now]){
		if(vis[i])continue;
		sum++;vis[i] = 1;
		dfs(i);
	}
} 
signed main(){
	cin>>n>>m>>a>>b;
	for(int i=1;i<=m;i++){
		int u,v;cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	int al=0;
	vis[a] = 1;
	vis[b] = 1;sum=2;
	dfs(a);al+=sum;al=n-al;
//	cout<<a
	memset(vis,0,sizeof vis);
	int br=0;
	sum=2;
	vis[a]=vis[b]=1;
	dfs(b);br+=sum;br=n-br;
	cout<<br*al;
	return 0; 
}

T4 Line

哎,不会就打暴力嘛,50分又不是接受不了~

Code

#include<bits/stdc++.h>
using namespace std;
const double INF=1e6;
const int FUCK_CCF=0;
const int N=2e5;
int n,m;
struct node{
	int w,b,v;
};
int ans,vis[N];
vector<node>g[N];
void dfs(int now,double sw,double bb){
//	cout<<now<<" ";
	if(now == n){
//		cout<<double(bb/sw);
		ans = max(ans,int(double(bb/sw)*INF));
		return;
	}
	for(node i:g[now]){
//		cout<<"$";
		if(vis[i.v])continue;
		vis[i.v] = 1;
		dfs(i.v,sw+i.w,(double)min(bb,(double)i.b));
		vis[i.v] = 0;
	}
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v,w,b;
		cin>>u>>v>>w>>b;
		g[u].push_back(node{w,b,v});
		g[v].push_back(node{w,b,u});
	}
	dfs(1,FUCK_CCF,0x3f3f3f);
	cout<<ans;
	return FUCK_CCF;
}

当然正解也有

日常犯懒

posted @ 2025-07-17 11:22  Venlacy  阅读(22)  评论(0)    收藏  举报