Simple record Part2.2

本文深入探讨了图论在解决计算机科学问题中的应用,包括最短路径、最小生成树、并查集等经典算法,并通过实例解析了它们在不同场景下的实现策略。文章覆盖了诸如寻找环路、构建最小连通子图、处理二分图等多个问题,展示了图论在解决复杂网络问题中的强大能力。
摘要由CSDN通过智能技术生成

1.CF25D Roads not only in Berland

Main idea:find all the loops and break it,connet all the components.

Thinking of problem solving:DSU on tree

For each edges,if the vertex u and v are already in the same connected components,record it and with set.After that,ergodic the set,find the components which it different from it and connect it.

#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=1005,inf=0x3f3f3f3f;
int f[maxn],n,m,vis[maxn],ans;
vector<int>e[maxn];
set<pair<int,int>>st;
struct w{
	int a,b,c,d;
}q[maxn];
int find(int x){
	if(f[x]==x)return x;
	else return f[x]=find(f[x]);
}
inline void hb(int x,int y){
	int fx=find(x);
	int fy=find(y);
	f[fx]=fy;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	for(int i=1;i<=n-1;i++){
		int u,v;
		cin>>u>>v;
		if(find(u)!=find(v))hb(u,v);
		else st.insert({u,v});
		
	}
	for(auto x:st){
		int fa=find(x.first);
		for(int i=1;i<=n;i++){
			if(find(i)==fa)continue;
			q[++ans]=((w){x.first,x.second,x.first,i});
			hb(x.first,i);
			break;
		}
	}
	cout<<ans<<endl;
	for(int i=1;i<=ans;i++){
		printf("%d %d %d %d\n",q[i].a,q[i].b,q[i].c,q[i].d);
	}
	return 0;
}

2.CF1205B Shortest Cycle

Main idea:You are given n n n integer numbers a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an . Consider graph on n n n nodes, in which nodes i i i , j j j ( i ≠ j i\neq j i=j ) are connected if and only if, a i a_i ai & a j ≠ 0 a_j\neq 0 aj=0

Thinking of problem solving:Floyd,Special judge.

According to the drawer principle,if the number of numbers greater than 128,there must be 3 numbers with 1 in its bitmask.So the answer will be 3.

If the number of numbers less than 128,we can ergodic all the numbers and connect them,and then run the floyd algorithm to find the smallest loop.

#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=130,inf=1e12;
int n,m;
int a[maxn];
int dis[maxn][maxn],mp[maxn][maxn];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		if(i>=128){
			cout<<3;
			return 0;
		}
		cin>>a[i];
		if(!a[i]){
			n--;
			i--;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j)continue;
			if((a[i]&a[j])){
				dis[i][j]=1;
				dis[j][i]=1;
				mp[i][j]=1;
				mp[j][i]=1;
			}
			else{
				dis[i][j]=inf;
				dis[j][i]=inf;
				mp[i][j]=inf;
				mp[j][i]=inf;
			}
		}
	}
	int ans=inf;
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(i==j||i==k||j==k)continue;
				ans=min(ans,dis[i][j]+mp[i][k]+mp[j][k]);
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			}
		}
	}
	if(abs(ans)<=inf/2)cout<<ans;
	else cout<<"-1";
	return 0;
}

3.CF1242B 0-1 MST

Main idea:find the number of connected components in the complement graph.

Thinking of problem solving:

First,chose the first vertex and mark all vertex connected to it,then combine all the vertex which has been not marked.

Second,traverse all the vertexes,for each vertex,mark all vertexes connected to it,and combine all the vertex which has been not marked like the first vertex.

In the end,traverse all vertexes and to count the answes.

#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m,k,t,cnt,tt;
int f[maxn],vis[maxn],v[maxn],size[maxn],du[maxn];
vector<int>e[maxn];
inline void add(int u,int v){
	e[u].push_back(v);
}
int find(int x){
	if(f[x]==x)return x;
	else return f[x]=find(f[x]);
}
inline void hb(int x,int y){
	f[find(x)]=find(y);
}
set<pair<int,int>>st;
signed main(){
	cin>>n>>m;
	int mm=inf;
	int id;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		add(u,v);
		add(v,u);
		du[u]++,du[v]++;
	}
	for(int i=1;i<=n;i++){
		if(du[i]<mm){
			mm=du[i];
			id=i;
		}
		f[i]=i;
	}
	for(auto x:e[id]){
		vis[x]=1;
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			hb(i,id);
		}
	}
	int tim=1;
	for(int i=1;i<=n;i++){
		if(!vis[i]||i==id)continue;
		tim++;
		memset(v,0,sizeof v);
		for(auto x:e[i]){
			v[x]=1;
		}
		for(int j=1;j<=n;j++){
			if(!v[j]){
				hb(i,j);
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(f[i]==i){
			cnt++;
		}
		size[find(i)]++;
	}
	for(int i=1;i<=n;i++){
		if(f[i]==i){
			st.insert({size[i],i});
		}
	}
	cout<<cnt-1<<endl;
	/*for(auto x:st){
		cout<<x.first<<" ";
	}*/
	return 0;
}



4.CF1307D Cow and Fields

Main idea:give you a bidirectional graph and a sequence with k vertex,you will chose two vertex from it and connect them,print the max value of shortest path from 1 to n.

Thinking of problem solving:pretreat the shortest path 1 to others and n to others,traverse all vertex and update the ans.

for(int i=1;i<=k-1;i++){
	ans=max(ans,dis1[a[i]]+dis2[a[i+1]]+1);
}

the new value of shortest path between 1 and n is min(ans,dis[1,n]).

#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m,k,dis1[maxn],dis2[maxn],a[maxn],vis[maxn];
vector<int>e[maxn];
inline void bfs1(){
	memset(vis,0,sizeof vis);
	dis1[1]=0;
	queue<int>q;
	q.push(1);
	vis[1]=1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(auto v:e[u]){
			if(vis[v])continue;
			dis1[v]=dis1[u]+1;
			vis[v]=1;
			q.push(v);
		}
	}
}
inline void bfs2(){
	memset(vis,0,sizeof vis);
	dis2[n]=0;
	queue<int>q;
	q.push(n);
	vis[n]=1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(auto v:e[u]){
			if(vis[v])continue;
			dis2[v]=dis2[u]+1;
			vis[v]=1;
			q.push(v);
		}
	}
}
int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	bfs1();
	bfs2();
	int res=dis1[n];
	sort(a+1,a+1+k,[](int x,int y){return dis1[x]<dis1[y];});
	int ans=-inf;
	for(int i=1;i<=k-1;i++){
		ans=max(ans,dis1[a[i]]+dis2[a[i+1]]+1);
	}
	cout<<min(ans,res);
}

5.CF1228D Complete Tripartite

Main idea:divide all vertex into tree sets,for each vertex in the a set,must be connected to all vertex of the other two sets,and there are no edges between two vertex in the same set.

Thinking of problem solving:coloring the graph,hash,mark each vertex’s degree.

#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=1e5+5,inf=0x3f3f3f3f;
int n,m;
int a[maxn*3],b[maxn*3];
int col[maxn],vis[maxn],cnt1,cnt2,cnt3,cnt;
int c1[maxn],c2[maxn],c3[maxn];
vector<int>e[maxn];
unordered_map<int,int>mp;
void dfs(int u,int fa){
	vis[u]=1;
	for(auto v:e[u]){
		if(v==fa)continue;
		if(!vis[v]){
			dfs(v,u);
		}
	}
}
int main(){
	cin>>n>>m;
	int flag=0;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		a[i]=u;
		b[i]=v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dfs(1,1);
	for(int i=1;i<=n;i++){
		cnt+=vis[i];
	}
	if(cnt!=n)flag=1;
	memset(vis,0,sizeof vis);
	for(auto v:e[1]){
		vis[v]=1;
	}
	for(int i=1;i<=n;i++){
		if(!vis[i])col[i]=1,c1[++cnt1]=i,mp[i]=1;
	}
	memset(vis,0,sizeof vis);
	for(int i=1;i<=n;i++){
		if(!col[i]){
			for(auto v:e[i]){
				if(col[v]==1)continue;
				col[v]=2;
				c2[++cnt2]=v;
				mp[v]=2;
			}
			break;
		}
	}
	memset(vis,0,sizeof vis);
	for(int i=1;i<=n;i++){
		if(!col[i]){
			col[i]=3;
			c3[++cnt3]=i;
			mp[i]=3;
		}
	}
	if(cnt1+cnt2+cnt3!=n)flag=1;
	memset(vis,0,sizeof vis);
	for(int i=1;i<=m;i++){
		int u=a[i];
		int v=b[i];
		vis[u]++,vis[v]++;
		if(mp[a[i]]==mp[b[i]]){
			flag=1;
			break;
		}
	}
	for(int i=1;i<=cnt1;i++){
		if(vis[c1[i]]!=cnt2+cnt3){
			flag=1;
			break;
		}
	}
	for(int i=1;i<=cnt2;i++){
		if(vis[c2[i]]!=cnt1+cnt3){
			flag=1;
			break;
		}
	}
	for(int i=1;i<=cnt3;i++){
		if(vis[c3[i]]!=cnt1+cnt2){
			flag=1;
			break;
		}
	}
	if(cnt1==0||cnt2==0||cnt3==0)flag=1;
	if(flag)puts("-1");
	else{
		for(int i=1;i<=n;i++){
			cout<<col[i]<<" ";
		}
	}
//	printf("%d %d %d",cnt1,cnt2,cnt3);
	return 0;
}

6.CF1245D Shichikuji and Power Grid

Main idea:you can choose some vertex to set site,and make all the vertex connect.
Thinking of problem solving:MST

#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=2005,inf=0x3f3f3f3f;
struct edge{
	int u,v,w;
}e[maxn*maxn];
struct node{
	int x,y;
}a[maxn];
int n,m,cnt,t,c[maxn],k[maxn],f[maxn];
vector<int>z;
vector<pair<int,int>>p;
int find(int x){
	if(f[x]==x)return x;
	else return f[x]=find(f[x]);
}
inline void hb(int x,int y){
	f[find(x)]=find(y);
}
inline int krus(){
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	sort(e+1,e+1+cnt,[](edge x,edge y){return x.w<y.w;});
	int res=0;
	int ans=0;
	for(int i=1;i<=cnt;i++){
		int u=find(e[i].u);
		int v=find(e[i].v);
		if(u==v)continue;
		hb(u,v);
		if(!e[i].u||!e[i].v){
			if(e[i].u)z.emplace_back(e[i].u);
			if(e[i].v)z.emplace_back(e[i].v);
		}
		else{
			p.push_back({e[i].u,e[i].v});
		}
		ans+=e[i].w;
		res++;
		if(res==n)return ans;
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].x>>a[i].y;
	}
	for(int i=1;i<=n;i++)cin>>c[i];
	for(int i=1;i<=n;i++)cin>>k[i];

	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j)continue;
			e[++cnt].u=i;
			e[cnt].v=j;
			e[cnt].w=(k[i]+k[j])*(abs(a[i].x-a[j].x)+abs(a[i].y-a[j].y));
		}
	}
	for(int i=1;i<=n;i++){
		e[++cnt].u=0;
		e[cnt].v=i;
		e[cnt].w=c[i];
	}
	cout<<krus()<<endl;
	cout<<z.size()<<endl;
	for(auto x:z){
		cout<<x<<" ";
	}
	cout<<endl;
	cout<<p.size()<<endl;
	for(auto x:p){
		printf("%lld %lld\n",x.first,x.second);
	}
	return 0;
}

7.CF1272E Nearest Opposite Parity

Main idea:You are given an array a a a consisting of n n n integers. In one move, you can jump from the position i i i to the position i − a i i - a_i iai (if 1 ≤ i − a i 1 \le i - a_i 1iai ) or to the position i + a i i + a_i i+ai (if i + a i ≤ n i + a_i \le n i+ain ).

For each position i i i from 1 1 1 to n n n you want to know the minimum the number of moves required to reach any position j j j such that a j a_j aj has the opposite parity from a i a_i ai (i.e. if a i a_i ai is odd then a j a_j aj has to be even and vice versa).

Thinking of problem solving:for each vertex,let the vertex after moving as its father,if it can reach the end for one move,add it into the queue.

start the bfs,for each vertex,if the vertex v’s distance has not been figured,and the value a [ u ] a[u] a[u] is equal to a [ v ] a[v] a[v],then the answer of vertex v -> d i s [ v ] dis[v] dis[v] is equal to d i s [ u ] + 1 dis[u]+1 dis[u]+1

#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m,cnt,t,a[maxn],dis[maxn];
vector<int>e[maxn];
void bfs(){
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(dis[i]==1){
			q.push(i);
		}
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(auto v:e[u]){
			if(dis[v]!=-1)continue;
			if((a[u]&1)==(a[v]&1))dis[v]=dis[u]+1;
			q.push(v);
		}
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		dis[i]=-1;
	}
	for(int i=1;i<=n;i++){
		if(i+a[i]>=1&&i+a[i]<=n){
			e[i+a[i]].emplace_back(i);
			if(((a[i]&1)^(a[i+a[i]]))&1)dis[i]=1;
		}
		if(i-a[i]>=1&&i-a[i]<=n){
			e[i-a[i]].emplace_back(i);
			if(((a[i]&1)^(a[i-a[i]]))&1)dis[i]=1;
		}
	}
	bfs();
	for(int i=1;i<=n;i++){
		cout<<dis[i]<<" ";
	}
	return 0;
}

8.CF558C Amr and Chemistry

Main idea:there are n numbers,you have two operations:
1.multiply a number by 2
2.divide a number by 2 and round it down.
output the number of operations make them equal.

thinking of problem solving:for each number,move it continuously until it reaches 0,and record each numbers in the middle.If there is an odd number,multiply it to the max and record it.

#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=1e5+5,inf=0x3f3f3f3f;
int n,m,cnt;
int a[maxn],mm,b[maxn],sum[maxn];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		mm=max(mm,a[i]);
	}
	for(int i=1;i<=n;i++){
		int x=a[i];
		int step=0,k=0,y=0,res=-1;
		while(x){
			if(res==0){
				b[x]++;
				sum[x]+=step;
				res=x&1;
				x/=2;
				step++;
			}
			else{
				y=x;
				k=0;
				while(y<=mm){
					b[y]++;
					sum[y]+=step+k;
					k++;
					y*=2;
				}
				res=x&1;
				x/=2;
				step++;
			}
		}
	}
	int ans=inf;
	for(int i=1;i<=mm;i++){
	//	printf("x = %d\n",sum[i]);
		if(b[i]>=n)ans=min(ans,sum[i]);
	}
	cout<<ans;
}

9.CF25C Roads in Berland

Main idea:in the process of adding edges,output the sum of shortest path of all vertex.

Thinking of problem solving:Floyd

#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=305,inf=0x3f3f3f3f;
int dis[maxn][maxn],n,m,q;
signed main(){
	cin>>n;
	memset(dis,inf,sizeof dis);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>dis[i][j];
		}
	}
	cin>>q;
	int res=0;
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			res+=dis[i][j];
		}
	}
	for(int x=1;x<=q;x++){
		int u,v,w;
		cin>>u>>v>>w;
		if(dis[u][v]>w)dis[u][v]=dis[v][u]=w;
		else{
			cout<<res<<" ";
			continue;
		}
		for(int k=1;k<=n;k++){
			if(k!=u&&k!=v)continue;
			for(int i=1;i<=n;i++){
				for(int j=1;j<=n;j++){
					dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
				}
			}
		}
		res=0;
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++){
				res+=dis[i][j];
			}
		}
		cout<<res<<" ";
	}
	return 0;
}

10.CF711D Directed Roads

Main idea:assign a direction of all edges,how many ways can I get rid of loops.

Thinking of problem solving:because there are only n edges and vertex,we can konw that must be a functional graph,in each loops,there are just two ways to make it a loop.According to the principle of multiplication,we can calculate the final formula:

a i a_i ai:edges in each loops

a n s = ans= ans= 2 n − ∑ a i 2^{n-\sum_{ }a_i} 2nai ∗ * ∏ 2 a i − 2 \prod_{}2^{a_i}-2 2ai2

#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f,mod=1e9+7;
int n,m,du[maxn],vis[maxn],f[maxn];
int res,ans=1;
vector<int>e[maxn];
map<pair<int,int>,int>mp;
inline int ksm(int a,int b){
	int k=1;
	while(b){
		if(b&1)k=k*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return k;
}
void topo(){
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(du[i]==1){
			q.push(i);
			vis[i]=i;
		}
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(auto v:e[u]){
			du[v]--;
			if(du[v]<=1&&!vis[v]){
				vis[v]=v;
				q.push(v);
			}
		}
	}
}
void dfs(int u,int fa){
	vis[u]=fa;
	f[fa]++;
	for(auto v:e[u]){
		if(!vis[v]&&du[v]>1){
			dfs(v,fa);
		}
	}
}
signed main(){
	cin>>n;
	int flag=0;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		e[i].emplace_back(x);
		e[x].emplace_back(i);
		du[i]++;
		du[x]++;
	}
	topo();
	for(int i=1;i<=n;i++){
		if(!vis[i]&&du[i]>1){
			dfs(i,i);
		}
	}
	for(int i=1;i<=n;i++){
		if(vis[i]==i&&f[i]==0){
			res++;
		//	printf(" f = %d i = %d,\n" ,f[i],i);
		}
		else if(vis[i]==i&&f[i]>1){
		//	printf(" f = %d i = %d,\n" ,f[i],i);
			ans=ans*(ksm(2,f[i])-2)%mod;
		}
	}
	cout<<ans*ksm(2,res)%mod;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值