比赛链接:

https://codeforces.com/contest/1702

E. Split Into Two Sets

题意:

\(n\)\(n\) 为偶数)张多米诺骨牌,每张骨牌有两个数字(数字范围为 1 到 \(n\)),问是否能将所有的骨牌分成两堆,每堆中的数字没有重复。

思路:

容易知道最后划分出来的两堆骨牌覆盖了 1 到 \(n\) 的所有数字,即每个数字在每堆中只有一个。
所以可以将所有的数字对应的骨牌编号记录下来,同一个数字对应的两张骨牌要不在一个堆中,满足二分图的性质。
将一张骨牌所对应的两个编号之间建立一条边,通过染色法判断是不是二分图。

#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
	LL n;
	cin >> n;
	vector <LL> g[n + 1], cnt(n + 1), G[n + 1];
	bool ok = true;
	for (int i = 0; i < n; i ++ ){
		LL a, b;
		cin >> a >> b;
		g[a].push_back(i);
		g[b].push_back(i);
		if (a == b){
			ok = false;
		}
		cnt[a] ++ ;
		cnt[b] ++ ;
		if (cnt[a] >= 3 || cnt[b] >= 3){
			ok = false;
		}
	}
	if (!ok){
		cout << "NO\n";
		return;
	}
	for (int i = 1; i <= n; i ++ ){  //建图
		LL u = g[i][0], v = g[i][1];
		G[u].push_back(v);
		G[v].push_back(u);
	}
	vector <LL> color(n + 1);
	function<bool(LL, LL)> dfs = [&] (LL u, LL c){  //染色法判断二分图
		color[u] = c;
		for (auto v : G[u]){
			if (!color[v]){
				if (!dfs(v, 3 - c))
					return false;
			}
			else{
				if (color[v] == c){
					return false;
				}
			}
		}
		return true;
	};
	for (int i = 1; i <= n; i ++ ){
		if (!color[i]){
			if (!dfs(i, 1)){
				cout << "NO\n";
				return;
			}
		}
	}
	cout << "YES\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T;
	cin >> T;
	while(T -- )
		solve();
	return 0;
}

让骨牌对应的两个数字产生联系,它们最后会形成一个环,要做的就是将这个环拆成两段,分别放到两个堆中,即环的长度为偶数,所以可以通过并查集来做,判断每个环的长度即可。

#include <bits/stdc++.h>
using namespace std;
#define LL long long
struct dsu{
	LL n;
	vector <LL> p, sz;
	dsu(LL n) : n(n){
		p.resize(n + 1);
		sz.resize(n + 1);
		iota(p.begin(), p.end(), 0LL);
		for (int i = 1; i <= n; i ++ )
			sz[i] = 1;
	}
	LL get(LL x){
		return (x == p[x] ? x : (p[x] = get(p[x])));
	}
	void unite(LL x, LL y){
		x = get(x);
		y = get(y);
		if (x != y){
			p[x] = y;
			sz[y] += sz[x];
		}
	}
};
void solve(){
	LL n;
	cin >> n;
	dsu d(n);
	vector <LL> cnt(n + 1);
	bool ok = true;
	for (int i = 0; i < n; i ++ ){
		LL x, y;
		cin >> x >> y;
		cnt[x] ++ ;
		cnt[y] ++ ;
		if (x == y){
			ok = false;
		}
		d.unite(x, y);
	}
	if (!ok){
		cout << "NO\n";
		return;
	}
	for (int i = 1; i <= n; i ++ ){
		if (cnt[i] != 2){
			cout << "NO\n";
			return;
		}
	}
	for (int i = 1; i <= n; i ++ ){
		if (i != d.get(i)){
			if (d.sz[d.get(i)] & 1){
				cout << "NO\n";
				return;
			}
		}
	}
	cout << "YES\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T;
	cin >> T;
	while (T -- )
		solve();
	return 0;
}

F. Equate Multisets

题意:

给定两个序列 \(a\)\(b\),可以选择 \(b\) 中的元素进行除 2 或者乘 2 操作,问是否能让 \(b\)\(a\) 中的元素相同。

思路:

对于一个元素 * 2 是不会改变奇偶性的,但是 / 2 可能会改变,可以将所有的偶数中的 2 全部去掉,这样子所有的元素就都是奇数了。
能够匹配的元素进行匹配,不能够匹配的一直 / 2,直到不能除为止,若始终无法匹配,说明没有办法让两个序列元素相同。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
	LL n;
	cin >> n;
	map <LL, LL> cnt;
	for (int i = 0; i < n; i ++ ){
		LL x;
		cin >> x;
		while(x % 2 == 0){
			x >>= 1;
		}
		cnt[x] ++ ;
	}
	vector <LL> b(n);
	for (int i = 0; i < n; i ++ ){
		cin >> b[i];
		while(b[i] % 2 == 0){
			b[i] >>= 1;
		}
	}
	for (int i = 0; i < n; i ++ ){
		while(cnt[b[i]] == 0 && b[i] > 0){
			b[i] /= 2;
		}
		if (b[i] == 0){
			cout << "NO\n";
			return;
		}
		cnt[b[i]] -- ;
	}
	cout << "YES\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T;
	cin >> T;
	while(T -- )
		solve();
	return 0;
}

G2. Passable Paths

题意:

有一个树,现在给定一个集合,问集合中的点是否在树的一条链上。

思路:

先随便将一个点作为树的根。
对于一个链,要确定它的两个端点。但是刚开始不知道,随便找两个点,设为 \(a\)\(b\)
现在考虑点 \(x\)
定义点 \(u\)\(v\) 之间的距离为 \(dis(u, v)\)
如果 \(dis(a, b) = dis(a, x) + dis(b, v)\),说明点 \(x\) 在两个端点之间。
如果 \(dis(a, x) = dis(a, b) + dis(b, x)\),说明点 \(b\)\(a\)\(x\) 之间,即 \(b\) 不是端点,要把端点改为 \(x\)
同理 \(dis(b, x) = dis(a, b) + dis(a, x)\),将 \(a\) 改为 \(x\)
其他情况说明点不在链上。
接下来看 \(dis(u, v)\) 怎么求,记 \(d(u)\) 为点 \(u\) 到根节点的距离,\(lca(u, v)\)\(u\)\(v\) 的最近公共祖先。
容易得到 \(dis(u, v) = d(u) + d(v) - 2 * d(lca(u, v))\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 2e5 + 10;
vector <LL> g[N];
LL lg[N], p[N][30], d[N], n, q;
void dfs(int u, int fa){
	p[u][0] = fa;
	d[u] = d[fa] + 1;
	for (int i = 1; i <= lg[d[u]]; i ++ )
		p[u][i] = p[p[u][i - 1]][i - 1];
	for (auto v : g[u])
		if (v != fa)
			dfs(v, u);
}
int lca(int x, int y){
	if(d[x] < d[y]) swap(x, y);
	while (d[x] > d[y])
		x = p[x][lg[d[x] - d[y]] - 1];
	if (x == y) return x;
	for (int k = lg[d[x]] - 1; k >= 0; k -- )
		if (p[x][k] != p[y][k]){
			x = p[x][k];
			y = p[y][k];
		}
	return p[x][0];
}
LL dis(LL a, LL b){
	return d[a] + d[b] - 2 * d[lca(a, b)];
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i ++ )
		lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
	for (int i = 0; i < n - 1; i ++ ){
		LL u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(1, 0);
	cin >> q;
	for (int i = 0; i < q; i ++ ){
		LL k;
		cin >> k;
		vector <LL> a(k);
		for (int j = 0; j < k; j ++ )
			cin >> a[j];
		if (k <= 2){
			cout << "YES\n";
			continue;
		}
		LL x = a[0], y = a[1];
		bool ok = true;
		for (int j = 2; j < k; j ++ ){
			if (dis(x, a[j]) + dis(a[j], y) == dis(x, y)) continue;
			else if (dis(x, a[j]) == dis(x, y) + dis(y, a[j])) y = a[j];
			else if (dis(y, a[j]) == dis(x, y) + dis(x, a[j])) x = a[j];
			else{
				ok = false;
				break;
			}
		}
		cout << (ok ? "YES\n" : "NO\n");
	}
	return 0;
}
posted on 2022-07-11 15:52  Hamine  阅读(60)  评论(0编辑  收藏  举报