比赛链接:
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;
}