图论刷题1

582div3 G. Path Queries

题意

给定一颗 n n n个点的加权树,以及 m m m次询问,每次询问输出存在简单路径中边权不大于 x x x的顶点对数

( 1 ≤ n , m ≤ 2 ⋅ 10 5 1 \le n, m \le 2 \cdot 10^5 1≤n,m≤2⋅105 )------树中的顶点数和查询数。 x ≤ 2 ⋅ 10 5 x \le 2\cdot10^5 x≤2⋅105

思路

可以发现 x x x越大则满足的点对数越多,所以考虑离线按 x x x从小到大处理,每次将所有边权小于 x x x的边加入,使用并查集维护互相可达的顶点数即可

代码

c 复制代码
#include<bits/stdc++.h>

#define ull unsigned long long 
#define ll long long
#define inf 1e9
#define INF 1e18
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl

using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));  

const int N=2e5+10;


struct edge{
   int u,v,w;
   bool operator <(const edge&t)const
   {
   	return w<t.w;
   }
};

struct query{
   int x,id;
   bool operator <(const query&t)const
   {
   	return x<t.x;
   }
};

int p[N];
int siz[N];

int find(int x)
{
   if(x!=p[x]) p[x]=find(p[x]);
   return p[x];
}

void merge(int x,int y)
{
   x=find(x);
   y=find(y);
   if(x!=y)
   {
   	p[x]=y;
   	siz[y]+=siz[x];
   	
   }
}

void solve()
{
   int n,m;
   cin>>n>>m;
   for(int i=1;i<=n;i++) {p[i]=i;siz[i]=1;}
   
   vector<edge>e(n);
   for(int i=1;i<n;i++)
   {
   	int a,b,c;
   	cin>>a>>b>>c;
   	e[i]={a,b,c};
   }
   sort(all(e));
   
   vector<query>q(m+1);
   for(int i=1;i<=m;i++) {cin>>q[i].x;q[i].id=i;}
   sort(all(q));
   
   vector<ll>ans(m+1);
   ll res=0;
   
   int j=1;
   for(int i=1;i<=m;i++)
   {
   	while(j<n && e[j].w<=q[i].x)
   	{
   		int u=e[j].u;
   		int v=e[j].v;
   		
   		u=find(u);
   		v=find(v);
   		
   		if(u!=v)
   		{
   			res+=1ll*siz[u]*siz[v];
   			merge(u,v);
   		}
   		j++;
   	}
   	ans[q[i].id]=res;
   }
   
   for(int i=1;i<=m;i++) cout<<ans[i]<<" ";
}
   
   
int main()
{
   ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
   
   solve();
   
   return 0;
}

516div1 B. Labyrinth

题意

在一个迷宫中,上下移动不受限制,但左右移动次数最多为 x , y x,y x,y,求最多可到达的单元格数

思路

一开始四维枚举所有状态,不出所料的t了,其实是 01 B F S 01 BFS 01BFS板子题,对于边权为 0 0 0的操作(上下移动)将其加到队头,边权为1(左右移动)则加入到队尾即可,因为这样保证一定是距离短的点先到

代码

c 复制代码
#include<bits/stdc++.h>

#define ull unsigned long long 
#define ll long long
#define inf 1e9
#define INF 1e18
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl

using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));  

int dx[]={0,1,0,-1};
int dy[]={-1,0,1,0};

void solve()
{
	int n,m,r,c,x,y;
	cin>>n>>m>>r>>c>>x>>y;
	
	vector<vector<char>>g(n+1,vector<char>(m+1));
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++) cin>>g[i][j];
	}
	
	deque<array<int,4>>q;
	vector<vector<int>>vis(n+1,vector<int>(m+1));
	
	q.push_back({r,c,x,y});
	int ans=0;
	
	while(!q.empty())
	{
		auto [x,y,ra,rb]=q.front();
		q.pop_front();
		
		
		if(vis[x][y] || ra<0 || rb<0)continue;
		vis[x][y]=1;
		ans++;
		
		for(int i=0;i<4;i++)
		{
			int a=x+dx[i];
			int b=y+dy[i];
			
			if(a<1 || a>n || b<1 || b>m) continue;
			if(vis[a][b]) continue;
			if(g[a][b]=='*') continue;
			
			if(i==1 || i==3) {q.push_front({a,b,ra,rb});continue;}
			if(i==0) {q.push_back({a,b,ra-1,rb});continue;}
			if(i==2) q.push_back({a,b,ra,rb-1});
		}
	}
	cout<<ans<<endl;
}
	
	
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	
	solve();
	
	return 0;
}

div1 599 B. 0-1 MST

题意

给定 n n n个点的完全图 n ≤ 10 5 n \leq 10^5 n≤105,以及 m m m条边,除了这 m m m条边的边权为1,其余边的边权都为0,求最小生成树边权

思路

因为是完全图,直接建图跑最小生成树显然不可取,不难发现答案其实为补图的连通块个数-1,因为补图的边权都为0,我们跑出补图的连通块个数即可(时间复杂度有点妙?)

代码

c 复制代码
#include<bits/stdc++.h>

#define ull unsigned long long 
#define ll long long
#define inf 1e9
#define INF 1e18
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl

using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));  

const int N=1e5+10;
set<int>e[N];


void solve()
{
	int n,m;
	cin>>n>>m;
	set<int>s;
	for(int i=1;i<=n;i++) s.insert(i);
	
	for(int i=1;i<=m;i++)
	{
		int a,b;
		cin>>a>>b;
		e[a].insert(b);
		e[b].insert(a);
	}
	
	int ans=0;
	auto dfs=[&](auto &&dfs,int u)->void
	{
		vector<int>v;
		for(auto t:s)
		{
			if(!e[t].count(u)) v.pb(t);//补图的点
		}
		
		for(auto t:v) s.erase(t);
		for(auto t:v) dfs(dfs,t);
	};
	
	for(int i=1;i<=n;i++)
	{
		if(s.count(i)) {ans++;dfs(dfs,i);}
	}
	
	cout<<ans-1<<endl;
	
	
}
	
	
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	
	solve();
	
	return 0;
}

div3 656 E. Directing Edges

题意

给定 n n n个点的图,包含有向边和无向边,你需要把所有无向边指定方向,使得图是一个有向无环图

思路

要使图中无环,则需要基于拓扑序来定向,即让拓扑序较小的边指向拓扑序较大的边即可,注意拓扑排序一定包含所有点,所以我们用有向图跑拓扑排序得到拓扑序列之后,根据拓扑序定向即可

代码

c 复制代码
#include<bits/stdc++.h>

#define ull unsigned long long 
#define ll long long
#define inf 1e9
#define INF 1e18
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl

using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));  

const int N=2e5+10;
vector<int>e[N];

void solve()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) e[i].clear();
	
	vector<int>din(n+1);
	vector<int>U(m+1),V(m+1);
	for(int i=1;i<=m;i++)
	{
		int a,b,c;
		cin>>a>>b>>c;
		if(a) e[b].pb(c);
		U[i]=b;
		V[i]=c;
		
	}
	vector<int>top;//top序列
	auto topsort=[&]()
	{
		
		for(int i=1;i<=n;i++)
		{
			for(auto ed:e[i]) din[ed]++;
		}
		queue<int>q;
		for(int i=1;i<=n;i++) if(!din[i]) q.push(i);
		
		while(!q.empty())
		{
			auto t=q.front();
			q.pop();
			top.pb(t);
			
			for(auto ed:e[t])
			{
				din[ed]--;
				if(!din[ed]) q.push(ed);
			}
		}
		return top.size()==n;
	};
	
	if(topsort())
	{
		yes;
		vector<int>rk(n+1);
		for(int i=0;i<n;i++) rk[top[i]]=i;
		for(int i=1;i<=m;i++)
		{
			if(rk[U[i]]>rk[V[i]]) swap(U[i],V[i]);
		}
		for(int i=1;i<=m;i++) cout<<U[i]<<" "<<V[i]<<endl;
	}
	else no;
	
	
}
	
	
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--)
	solve();
	
	return 0;
}
相关推荐
r0ysue_2 小时前
02.上帝之心算法用GPU计算提速50倍
算法·gpu
L_cl2 小时前
【Python 算法零基础 4.排序 ⑦ 桶排序】
数据结构·算法·排序算法
小O的算法实验室3 小时前
2025年AIR SCI1区TOP,多策略增强蜣螂算法MDBO+实际工程问题,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
花自向阳开10243 小时前
LeetCode hot100-11
数据结构·算法·leetcode
月亮被咬碎成星星3 小时前
LeetCode[404]左叶子之和
算法·leetcode
有梦想的骇客3 小时前
书籍在其他数都出现k次的数组中找到只出现一次的数(7)0603
算法
jiet_h4 小时前
Android Kotlin 算法详解:链表相关
android·算法·kotlin
数据潜水员5 小时前
C#基础语法
java·jvm·算法
鸽子炖汤5 小时前
LRC and VIP
c++·算法·图论
鑫鑫向栄5 小时前
[蓝桥杯]机器人塔
数据结构·c++·算法·蓝桥杯