首页 > 其他分享 >20240309 专项训练

20240309 专项训练

时间:2024-03-10 11:14:37浏览次数:18  
标签:专项 le 训练 int 20240309 st ch low dfn

图论(拓扑、强连通分量)专项训练

以下算法若无特殊提及,复杂度一般都为 \(\mathcal{O}(n + m)\) 水平。

study

link
有 \(n\) 个项目,对于某些项目 \(x\) 和 \(y\) ,必须先学完 \(x\) 再开始学 \(y\)。请问能否完成所有项目的学习。

对于 \(30\%\) 的数据,保证 \(1 \le n, m \le 15\)。

对于 \(70\%\) 的数据,保证 \(1 \le n, m \le 1000\)。

对于 \(100\%\) 的数据,保证 \(1 \le n, m \le 10^5, 1 \le T \le10\)。

100 pts 做法

建图后,跑拓扑排序,确定图为 DAG 即可。

#include<bits/stdc++.h>
#define ll long long
#define N 100005
using namespace std;
ll read(){
	ll x = 0, f = 1; char ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
	return x * f;
}
int T, n, m, d[N];
vector < int > G[N];
void work(){
	queue < int > q;
	n = read(), m = read();
	memset(d, 0, sizeof d);
	vector < int > t;
	for(int i = 1; i <= n; i++) G[i] = t;
	for(int i = 1; i <= m; i++){
		int u = read(), v = read();
		G[u].push_back(v); d[v]++;
	}
	int cnt = 0;
	for(int i = 1; i <= n; i++)
	  if(d[i] == 0) q.push(i);
	while(!q.empty()){
		int u = q.front(); q.pop(); cnt++;
		for(auto v : G[u]) if(--d[v] == 0) q.push(v);
	}
	if(cnt != n) printf("no\n");
	else printf("yes\n");
}
int main(){
	freopen("study.in", "r", stdin);
	freopen("study.out", "w", stdout);
	T = read();
	while(T--) work();
	return 0;
}

star

link
牛栏里有 \(n\) 头奶牛,给定若干组 \((u, v)\) 表示奶牛 \(u\) 喜欢 \(v\),并且如果 A 喜欢 B,B 喜欢 C,那么 A 也喜欢 C,请你算出有多少头奶牛被所有奶牛喜欢。

对于 \(30\%\) 的数据,保证 \(n \le 20, m \le 50\)。

对于 \(70\%\) 的数据,保证 \(n \le 1000, m \le 10^4\)。

对于 \(100\%\) 的数据,保证 \(n \le 10^5, m \le 5 \times 10^5\)。

100 pts 做法

经典奶牛题。tarjan 缩点后形成一张有向无环图。

如果存在两个以上出度为 \(0\) 的点就不存在“明星奶牛”。

否则唯一出度为 \(0\) 的点中的点数就是答案。

#include<bits/stdc++.h>
#define ll long long
#define N 100005
using namespace std;
int read(){
	int x = 0, f = 1; char ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
	return x * f;
}
int n, m, belong[N], dfn[N], low[N];
int cnt, k, num[N], d[N];
bool instack[N];
vector < int > H[N];
stack < int > st;
void tarjan(int u){
	dfn[u] = low[u] = ++cnt;
	st.push(u); instack[u] = true;
	for(auto v : H[u]){
		if(!dfn[v]){
			tarjan(v); low[u] = min(low[u], low[v]);
		} else if(instack[v]) low[u] = min(low[u], dfn[v]);
	}
	if(dfn[u] == low[u]){
		k++;
		while(st.top() != u){
			int v = st.top(); st.pop();
			belong[v] = k; num[k]++;
			instack[v] = false;
		}
		int v = st.top(); st.pop();
		belong[v] = k; num[k]++;
		instack[v] = false;
	}
	return;
}
int main(){
	freopen("star.in", "r", stdin);
	freopen("star.out", "w", stdout);
	n = read(), m = read();
	for(int i = 1; i <= m; i++){
		int u = read(), v = read();
		H[u].push_back(v);
	}
	for(int i = 1; i <= n; i++)
	  if(!dfn[i]) tarjan(i);
	for(int u = 1; u <= n; u++){
		for(auto v : H[u])
		  if(belong[u] != belong[v]) d[belong[u]]++;
	}
	int ans = 0, ans1 = 0;
	for(int i = 1; i <= k; i++){
		if(d[i] == 0) ans += num[i], ans1++;
	}
	if(ans1 == 1) printf("%d\n", ans);
	else printf("0\n");
	return 0;
}

t3

link
在一个有向图中,有 \(n\) 个顶点,给出 \(m\) 对数字 \((u, v)\) 表示需要存在一条从顶点 \(u\) 走到顶点 \(v\) 的路径。让你构造一个这样的图,输出最少需要多少条边。

对于 \(30\%\) 的数据,保证 \(1 \le n \le 5\)。

对于 \(70\%\) 的数据,保证 \(1 \le n \le 200\)。

对于 \(100\%\) 的数据,保证 \(1 \le n, m \le 10^5\)。

30 pts 做法

枚举 \(n\) 个点所有连边情况。复杂度 \(\mathcal{O}(2^m)\),\(m = n(n-1)/2\)。

100 pts 做法

path

link
给定一张 \(n\) 个点, \(m\) 条边的有向图,你需要判断是否存在两个点 \(u,v\),使得不存在从 \(u\) 到 \(v\) 的路径,也不存在从 \(v\) 到 \(u\) 的路径。

对于 \(30\%\) 的数据,保证 \(n \le 100, m \le 300\)。

对于 \(70\%\) 的数据,保证 \(n \le 1000, m \le 5000\)。

对于 \(100\%\) 的数据,保证 \(n \le 5 \times 10^4, m \le 10^5, T \le 10\)。

100 pts 做法 1

一眼缩点。然后先考虑一种暴力做法。

用 \(\texttt{bitset}\) 记录每个点能否走到当前点。正反图各跑一遍,如果两次跑出来结果之和不为缩点后的点数的话,那么就说明一定存在一个点不能到达另一个点。

实测该算法在 luogu \(\texttt{C++14} + \texttt{O2}\) 情况下可通过,学校 OJ 被卡。

注意多测清空

#include<bits/stdc++.h>
#define mp make_pair
#define ll long long
#define N 50005
using namespace std;
ll read(){
	ll x = 0, f = 1; char ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
	return x * f;
}
int n, m, belong[N], cnt;
int k, dfn[N], low[N], d[N];
int d1[N], f11[N];
bool instack[N];
bitset < N > f1[N];
vector < int > G[N], H[N], G1[N];
stack < int > st;
void tarjan(int u){
	dfn[u] = low[u] = ++cnt;
	st.push(u); instack[u] = true;
	for(auto v : H[u]){
		if(!dfn[v]){
			tarjan(v); low[u] = min(low[u], low[v]);
		} else if(instack[v]) low[u] = min(low[u], dfn[v]);
	}
	if(dfn[u] == low[u]){
		k++;
		while(st.top() != u){
			int v = st.top(); st.pop();
			belong[v] = k;
			instack[v] = false;
		}
		int v = st.top(); st.pop();
		belong[v] = k;
		instack[v] = false;
	}
	return;
}
void work(){
	n = read(), m = read();
	memset(dfn, 0, sizeof dfn);
	memset(low, 0, sizeof low);
	vector < int > t; k = cnt = 0;
	for(int i = 1; i <= n; i++) G[i] = t, H[i] = t, G1[i] = t;
	for(int i = 1; i <= m; i++){
		int u = read(), v = read();
		H[u].push_back(v);
	}
	for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
	for(int u = 1; u <= n; u++){
		for(auto v : H[u])
		  if(belong[u] != belong[v]){
		  	G[belong[u]].push_back(belong[v]);
		  	G1[belong[v]].push_back(belong[u]);
		  	d[belong[v]]++; d1[belong[u]]++;
		  }
	}
	queue < int > q;
	for(int i = 1; i <= k; i++) f1[i].reset(), f1[i][i] = true;
	for(int i = 1; i <= k; i++)
	  if(d[i] == 0) q.push(i);
	while(!q.empty()){
		int u = q.front(); q.pop();
		for(auto v : G[u]){
			f1[v] |= f1[u];
			if((--d[v]) == 0) q.push(v);
		}
	}
	for(int i = 1; i <= k; i++) f11[i] = f1[i].count();
	for(int i = 1; i <= k; i++) f1[i].reset(), f1[i][i] = true;
	for(int i = 1; i <= k; i++) if(d1[i] == 0) q.push(i);
	while(!q.empty()){
		int u = q.front(); q.pop();
		for(auto v : G1[u]){
			f1[v] |= f1[u];
			if((--d1[v]) == 0) q.push(v);
		}
	}
	bool flag = false;
	for(int i = 1; i <= k; i++)
	  if(f11[i] + f1[i].count() != k + 1) flag = true;
	if(k == 1) flag = false;
	if(flag) printf("yes\n");
	else printf("no\n");
}
int main(){
	freopen("path.in", "r", stdin);
	freopen("path.out", "w", stdout);
	int T = read();
	while(T--) work();
	return 0;
}

100 pts 做法 2

考虑更加简洁的做法。

如果存在两个点同时称为入度为 \(0\) 的点的话,那么就一定存在。

注:关键在 if(!q.empty()) return false;

#include<bits/stdc++.h>
#define mp make_pair
#define ll long long
#define N 50005
using namespace std;
ll read(){
	ll x = 0, f = 1; char ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
	return x * f;
}
int n, m, belong[N], cnt;
int k, dfn[N], low[N], d[N];
bool instack[N];
vector < int > G[N], H[N];
stack < int > st;
void tarjan(int u){
	dfn[u] = low[u] = ++cnt;
	st.push(u); instack[u] = true;
	for(auto v : H[u]){
		if(!dfn[v]){
			tarjan(v); low[u] = min(low[u], low[v]);
		} else if(instack[v]) low[u] = min(low[u], dfn[v]);
	}
	if(dfn[u] == low[u]){
		k++;
		while(st.top() != u){
			int v = st.top(); st.pop();
			belong[v] = k;
			instack[v] = false;
		}
		int v = st.top(); st.pop();
		belong[v] = k;
		instack[v] = false;
	}
	return;
}
bool topo(){
	queue < int > q;
	int tot = 0;
	for(int i = 1; i <= k; i++)
	  if(!d[i]) q.push(i);
	while(!q.empty()){
		int u = q.front(); q.pop();
		if(!q.empty()) return false;
		tot++;
		for(auto v : G[u])
		  if(!(--d[v])) q.push(v);
	}
	if(tot == k) return true;
	return false;
}
void work(){
	n = read(), m = read();
	memset(dfn, 0, sizeof dfn);
	memset(low, 0, sizeof low);
	memset(d, 0, sizeof d);
	vector < int > t; k = cnt = 0;
	for(int i = 1; i <= n; i++)
	  G[i] = t, H[i] = t;
	for(int i = 1; i <= m; i++){
		int u = read(), v = read();
		H[u].push_back(v);
	}
	for(int i = 1; i <= n; i++)
	  if(!dfn[i]) tarjan(i);
	for(int u = 1; u <= n; u++){
		for(auto v : H[u])
		  if(belong[u] != belong[v]){
		  	G[belong[u]].push_back(belong[v]);
		  	d[belong[v]]++;
		  }
	}
	if(topo()) printf("no\n");
	else printf("yes\n");
}
int main(){
	freopen("path.in", "r", stdin);
	freopen("path.out", "w", stdout);
	int T = read();
	while(T--) work();
	return 0;
}

标签:专项,le,训练,int,20240309,st,ch,low,dfn
From: https://www.cnblogs.com/hayzxjr/p/18063067

相关文章

  • 20240309
    瑞士轮思路:快排会g,所以要归并排序defineintlonglong会g,关掉快排函数:stable_sort,用法和sort一样#include<bits/stdc++.h>usingnamespacestd;//#defineintlonglongstructinf{intscore;intid;intforce;};boolcmp(infa,infb){if......
  • 代码随想录算法训练营第四十一天|01背包问题, 01背包问题—— 滚动数组,分割等和子集
    01背包问题,你该了解这些! 题目链接:46.携带研究材料(第六期模拟笔试)(kamacoder.com)思路:第一次遇到背包问题,好好记住吧。代码随想录(programmercarl.com)#include<bits/stdc++.h>usingnamespacestd;intmain(){intm,n;cin>>m>>n;vector<int>z(m);vec......
  • python Ai 应用开发基础训练,字符串,字典,文件
    --------------------------------------  编程能是大模型应用的天花板..................................................................所以要好好将大模型应用在企业一定要好好练好最看不起的一环,基础能力字符串处理 本文档来自老男孩培训Alex课程记录,我在2017年......
  • 预训练
    2024.3.7预训练1.预训练有什么用机器学习:偏数学(《统计学习方法》-李航)深度学习(人工智能)的项目:大数据支持(主流)我们首先介绍下卷积神经网络(CNN),CNN一般用于图片分类任务,并且CNN由多个层级结构组成,不同层学到的图像特征也不同,越浅的层学到的特征越通用(横竖撇捺),越深的层学......
  • 3/9 训练笔记
    P5268[SNOI2017]一个简单的询问题解不妨把每个区间表示成\(|V|\)维向量\(b\)的形式,其中\(b[i]\)为在区间\([l,r]\)中,\(i\)出现的次数。然后我们发现要求的实际上是\(a\cdotb\)。拆一下(这里用\(g(i)\)表示\([1,i]\)的向量):\(a\cdotb=[l_1,r_1]\cdot[l_......
  • 代码随想录算法训练营第四天| 24. 两两交换链表中的节点 19.删除链表的倒数第N个节点
    24.两两交换链表中的节点https://leetcode.cn/problems/swap-nodes-in-pairs/description/publicListNodeswapPairs(ListNodehead){if(head==null||head.next==null)returnhead;ListNoderes=head.next;ListNodepre=newListNod......
  • 代码随想录算法训练营day17 | leetcode 110. 平衡二叉树、257. 二叉树的所有路径、404
    目录题目链接:110.平衡二叉树-简单题目链接:257.二叉树的所有路径-简单题目链接:404.左叶子之和-简单题目链接:110.平衡二叉树-简单题目描述:给定一个二叉树,判断它是否是平衡二叉树示例1:输入:root=[3,9,20,null,null,15,7]输出:true示例2:输入:root=[1,2,2,3,3,null,nul......
  • 2023牛客暑期多校训练营2 B Link with Railway Company
    ProblemDescription给你一个\(n\)个节点的树状铁路网络,维护一条边每天需要花费\(c_i\)代价。现在有\(m\)条从\(a_i\)到\(b_i\),每天的盈利为\(x_i\),维护花费为\(y_i\)的路线可以运营。你可以选择一部分路线运营,求每日的最大收益。Input第一行输入两个整数\(n,......
  • 3/8 训练笔记
    闲话排查许久后发现:intvis[20000010]->aclonglongvis[20000010]->mle并且开了dill所以查了挺久。一个诡异的bug是:...;debug(f,g)->ac...;->wa最后发现vectorresize小了。并且不知道为什么debug一下就好了。P3702[SDOI2017]序列计数题解考虑......
  • 代码随想录算法训练营第四十天|● 343. 整数拆分 ● 96.不同的二叉搜索树
    整数拆分 题目链接:343.整数拆分-力扣(LeetCode)思路:第一步想的是用递归做,intdigui(intn){if(n==1)returnn;returnmax((n/2)*(n-n/2),digui(n/2)*digui(n-n/2));}可惜的是题目并没有规定一定要分成两份,因此这个思路是不对的,但已经初窥门径。......