2026牛客寒假训练营-Day2 JEDC

2026牛客寒假训练营-Day2 JEDC

Problem J ------ 终于再见

  • 城市的等级至多有 sqrt(m)sqrt(m)sqrt(m) 种
  • 故枚举城市等级后用bfs求距离,总复杂度 O(n∗sqrt(m))O(n*sqrt(m))O(n∗sqrt(m))
cpp 复制代码
#include<bits/stdc++.h>
#define int long long

using namespace std;



void solve(){
	int n,m;
	cin >> n >> m;
	vector<vector<int>> v(n+1);
	vector<int> deg(n+1);
	for(int i = 1; i <= m; i ++){
		int x,y;
		cin >> x >> y; 
		deg[x] ++,deg[y] ++;
		v[x].push_back(y);
		v[y].push_back(x);
	}
	vector<int> dist(n+1,1e18),ans(n+1,1e18);
	vector<vector<int>> p(m+1);
	vector<int> k;
	queue<int> q;
	for(int i = 1; i <= n; i ++){
		p[deg[i]].push_back(i);
	}
	for(int i = m; i >= 1; i --){
		if(p[i].size() == 0) continue;
		fill(dist.begin(),dist.end(),1e18);
		k.insert(k.end(),p[i].begin(),p[i].end());
		queue<int> q;
		for(int j : k){
			dist[j] = 0;
			q.push(j);
		}
		while(q.size()){
			int t = q.front();
			q.pop();
			for(int j : v[t]){
				if(dist[j] > dist[t] + 1){
					dist[j] = dist[t] + 1;
					q.push(j);
				}
			}
		}
		for(int j = 1; j <= n; j ++){
			if(deg[j] < i){
				ans[j] = min(ans[j],dist[j]);
			}
		}
	}
	for(int i = 1; i <= n; i ++){
		if(ans[i] < 1e17){
			cout << ans[i] << ' ';
		}else{
			cout << -1 << ' ';
		}
	}
	cout << '\n';
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t = 1;
	//cin >> t;
	while(t --){
		solve();
	}
}

Problem E ------ 01矩阵

  • 一种做法是直接构造,可行的矩阵类似下图

    00
    01

    000
    011
    010

    0000
    0111
    0100
    0101

    00000
    01111
    01000
    01011
    01010

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

using namespace std;

int main(){
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            cout << "10"[min(i, j) & 1];
        }
        cout << endl;
    }
}
  • 另一种做法是找到一个大小为 nnn 的基矩阵后,进行拓展,拓展方法有两种。

    • 在基矩阵的最右边添加一列全为1的列,然后再最下方添加全0的行
    • 在基矩阵的最下方添加一列全为1的行,然后再最右边添加全0的列
  • 拓展的例子如下:

    n = 6
    101111
    000001
    101011
    000011
    100011
    000000

    n = 7
    1011110
    0000010
    1010110
    0000110
    1000110
    0000000
    1111110

    n = 8
    10111101
    00000101
    10101101
    00001101
    10001101
    00000001
    11111101
    00000000

    n = 9
    101111010
    000001010
    101011010
    000011010
    100011010
    000000010
    111111010
    000000000
    111111110

cpp 复制代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

int a[6][6] = {
	{1,0,1,1,1,1},
	{0,0,0,0,0,1},
	{1,0,1,0,1,1},
	{0,0,0,0,1,1},
	{1,0,0,0,1,1},
	{0,0,0,0,0,0},
};

void solve(){
	int n;
	cin >> n;
	if(n == 1){
		cout << 0 << '\n';
	}else if(n == 2){
		cout << "01\n00\n";
	}else if(n == 3){
		cout << "000\n101\n001\n";
	}else if(n == 4){
		cout << "0001\n0000\n0011\n1011\n";
	}else if(n == 5){
		cout << "10000\n10010\n00000\n11010\n11011\n";
	}else{
		vector<vector<int>> ans(n+1,vector<int>(n+1));
		for(int i = 1; i <= 6; i ++)
			for(int j = 1; j <= 6; j ++){
				ans[i][j] = a[i-1][j-1];
			}
		bool clk = 1; 
		for(int i = 7; i <= n; i ++){
			if(clk){
				for(int j = 1; j <= i - 1; j ++){
					ans[i][j] = 1;
				}
				for(int j = 1; j <= i;j ++){
					ans[j][i] = 0;
				}
			}else{
				for(int j = 1; j <= i; j ++){
					ans[i][j] = 0;
				}
				for(int j = 1; j <= i - 1;j ++){
					ans[j][i] = 1;
				}
			}
			clk = !clk;
		}
		for(int i = 1; i <= n; i ++){
			for(int j = 1; j <= n; j ++){
				cout << ans[i][j];
			}
			cout << '\n';
		}
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t = 1;
	//cin >> t;
	while(t --){
		solve();
	}
}

Problem D ------ 数字积木

  • 本题的粗略思路是:

    1. 求 mex = k 的连通子图个数。
    2. 枚举 mex=1,2,3...nmex = 1,2,3...nmex=1,2,3...n , 按步骤 111 的方法求出子图数,并求和。
    • 对于步骤1,解决方法是先删掉点k,然后求出"最小连通骨架",即求找到一个包含点 111 ~ 点 k−1k-1k−1所有点的最小连通子图,从该子图中中删除任何一个点,mex 都不再是 k。
    • 求"最小连通骨架"的方法是,固定一个始终在连通骨架中的点(即点1)为根,新加入一个点到骨架中时,从新加入的点开始,往根移动,路上经过的所有点都要被加入连通骨架。
    • 那么骨架中的点必须在最后的联通子图中,非骨架中的点可有可无,按树形dp统计非骨架中的点,有多少种可能的形状即可。
    • 但是这个做法是 O(n2)O(n^2)O(n2)的,需要降复杂度。
    • 降复杂度的方法是利用 单调性
  • 本题的最终思路:

    • 从1~n枚举mex时,"连通骨架"的大小有只会随着点的加入越来越大,具有 单调性
    • 新加入的点,会导致部分的点从"非骨架"中,被纳入"连通骨架"从而使得子图个数数减少。
    • 重点关注本轮新纳入的点,除掉它们对答案的贡献,即可通过上一轮的答案得到本轮的答案
    • 总复杂度 O(n)O(n)O(n)
  • 注意处理 0的逆元 ,方法是单独开数组记录 0 因子个数,子树有 0 因子时,不计算贡献。

cpp 复制代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int mod = 1e9 + 7;

int qmi(int a,int b){
	int ret = 1;
	while(b){
		if(b & 1) ret = (1ll * ret * a) % mod;
		b >>= 1;
		a = (1ll * a * a) % mod;
	}
	return ret;
}

void solve(){
	int n;
	cin >> n;
	vector<int> a(n+1),ap(n+1);
	for(int i = 1; i <= n; i ++){
		cin >> a[i];
		ap[a[i]] = i;
	}
	vector<vector<int>> v(n+1);
	for(int i = 1; i <= n - 1; i ++){
		int x,y;
		cin >> x >> y;
		v[x].push_back(y);
		v[y].push_back(x);
	}
	vector<int> dp(n+1),fa(n+1),zero(n+1);
	int root = ap[0];
	auto dfs = [&](auto &&self,int u,int p)->void{
		fa[u] = p;
		dp[u] = 1;
		zero[u] = 0;
		for(int i : v[u]){
			if(i != p){
				self(self,i,u);
				if((dp[i] + 1) % mod == 0){
					zero[u] ++;
				}else if(zero[i] == 0){
					dp[u] *= (dp[i] + 1);
					dp[u] %= mod;
				}
			}
		}
		return;
	};
	dfs(dfs,root,-1);
	vector<int> done(n+1),g(n+2);
	done[ap[0]] = 1;
	int cnt = dp[root] % mod;
	int sum0 = zero[root];
	for(int i = 1; i <= n - 1; i ++){
		if(sum0 == 0) g[i] = cnt;
		else g[i] = 0;
		
		if(done[ap[i]]) continue;
		
		int p = ap[i];
		while(!done[p]){
			done[p] = 1;
			if((dp[p] + 1) % mod == 0){
				sum0 --;
			}else if(zero[p] == 0){
				cnt = (cnt * qmi(((dp[p] + 1) % mod),mod-2)) % mod;
			}
			sum0 += zero[p]; 
			cnt = (cnt * dp[p]) % mod;
			p = fa[p];
		}
	}
	if(sum0 == 0) g[n] = cnt;
	else g[n] = 0;
	
	int ret = 0;
	for(int i = 1; i <= n; i ++){
		ret += (g[i] - g[i+1] + mod) % mod * i;
		ret %= mod;
	}
	cout << ret << '\n';
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t = 1;
	//cin >> t;
	while(t --){
		solve();
	}
}

Problem C ------ 炮火轰炸

  • 重点关注和炮火点相邻的点,避免复杂度超限。
  • 本题的做法是,把没有形成包围圈的无害炮火点,标记出来(实际上标记的无害炮火点周围的点,但不重要)。
  • 如何标记无害炮火点周围的点?通过DFS实现,如果能从一个安全点走到该点,那么该点也是安全的。那么只需要找到第一个安全的点,再DFS即可。
  • 如何找到第一个安全的点?假设全局最右下角的一个炮火点坐标为 (i,j)(i,j)(i,j) , 那么点 (i+1,j+1)(i+1,j+1)(i+1,j+1) 肯定是安全的。
  • 单纯的DFS是超时的,如何优化? 遍历时,只关心与炮火点相邻的点。我们需要标记无害炮火点,如果一个点不和炮火点相邻,那么他不具备标记作用,不走到该点。
  • 对于一个询问,如果能走直线走到无穷远,或者能走到被标记过的无害的炮火点(周围),那么答案是"NO",否则就走到了有害炮火点,答案是是"YES"。
cpp 复制代码
#include<bits/stdc++.h>
#define int long long

using namespace std;
typedef pair<int,int> PII;

vector<int> dx = {-1,-1,-1,0,0,1,1,1},dy = {-1,0,1,-1,1,-1,0,1};
vector<int> dx2 = {1,-1,0,0},dy2 = {0,0,1,-1};
void solve(){
	int n,q;
	cin >> n >> q;
	vector<PII> a(n+1);
	vector<map<int,int>> mp1(1e6+1,map<int,int>()),mp2(1e6+1,map<int,int>());
	set<pair<int,int>> boom;
	auto check = [&](int x,int y) -> bool{
		auto p1 = mp1[x].lower_bound(y),p2= mp2[y].lower_bound(x);
		if(p1 == mp1[x].end() || p2 == mp2[y].end()) return true;
		if(p1->second == 0 && p2 -> second == 0) return false;
		else return true;
	};
	auto dfs = [&](auto &&self,int x,int y)->void{
		if(mp1[x].find(y) != mp1[x].end()) return;
		mp1[x][y] = 1;
		mp2[y][x] = 1;
		for(int i = 0; i < 4; i ++){
			int nx = x + dx2[i],ny = y + dy2[i];
			for(int j = 0; j < 8; j ++){
				int nnx = nx + dx[j],nny = ny + dy[j];
				if(boom.find({nnx,nny}) != boom.end()){
					self(self,nx,ny);
					break;
				}
			}
			
		}
	};
	
	for(int i = 1; i <= n; i++){
		cin >> a[i].first >> a[i].second;
		mp1[a[i].first][a[i].second] = 0;
		mp2[a[i].second][a[i].first] = 0;
		boom.insert(a[i]);
	}	
	sort(a.begin()+1,a.end(),greater<PII>());
	for(int i = 1; i <= n; i ++){
		//cout << i << endl;
		auto [x,y] = a[i];
		if(check(x+1,y+1)) dfs(dfs,x+1,y+1);
	}
	
	while(q --){
		int x,y;
		cin >> x >> y;
		if(check(x,y)){
			cout << "NO\n";
		}else{
			cout << "YES\n";
		}
	}
	
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	//cin >> t;
	while(t --){
		solve();
	}
}
相关推荐
寻寻觅觅☆9 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
偷吃的耗子10 小时前
【CNN算法理解】:三、AlexNet 训练模块(附代码)
深度学习·算法·cnn
2013编程爱好者10 小时前
【C++】树的基础
数据结构·二叉树··二叉树的遍历
NEXT0610 小时前
二叉搜索树(BST)
前端·数据结构·面试
化学在逃硬闯CS10 小时前
Leetcode1382. 将二叉搜索树变平衡
数据结构·算法
ceclar12311 小时前
C++使用format
开发语言·c++·算法
Gofarlic_OMS11 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
夏鹏今天学习了吗11 小时前
【LeetCode热题100(100/100)】数据流的中位数
算法·leetcode·职场和发展
忙什么果12 小时前
上位机、下位机、FPGA、算法放在哪层合适?
算法·fpga开发
董董灿是个攻城狮12 小时前
AI 视觉连载4:YUV 的图像表示
算法