Codeforces Round #805 (Div. 3)
Codeforces Round #805 (Div. 3)
这场切的真爽,感觉后面题偏简单了。
ABCD
C 维护数字第一次和最后出现位置。
D 贪心从最大的开始删,每次删全部,如果删多了,说明只要删当前字符一部分就好,这个二分找一下。
E
题意
\(n\) 张多米诺牌,正反两个数字,范围 \([1,n]\)。现在要求对它们分为两组,使得它们每组都有\([1,n]\) 中所有数字。
思路
尝试贪心无果后,考虑建边。我们对每个牌正反面的数字建边。
首先,显然每个数字必须出现 \(2\) 次,也就是每个点度数为 \(2\)。
然后,如果我们可以把这些牌的正反面分为两个部分,也就是一个二分图。那么我们就可以安排出一组解,又因为度数为 \(2\),第二组解自然也安排出来了。
反之,如果不是二分图,那么一定有奇环,这意味着会有“一个数字同时在正反两面” 的情况。显然这是不合法的。
由上只要对图和度数做判定即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int n; cin >> n;
vector<vector<int>> adj(n + 1);
bool ok = 1;
vector<int> du(n + 1);
map<PII,bool> mp;
for(int i = 0;i < n;i ++) {
int u,v; cin >> u >> v;
if(u == v) {
ok = 0;
}
if(u > v) swap(u,v);
du[u] += 1,du[v] += 1;
adj[u].push_back(v);
adj[v].push_back(u);
}
for(int i = 1;i <= n;i ++) if(du[i] != 2) ok = 0;
if(!ok) {
cout << "NO\n";
return;
}
vector<int> col(n + 1);
function<bool(int,int)> dfs = [&](int u,int c) {
col[u] = c;
for(auto v : adj[u]) {
if(col[u] == col[v]) return false;
if(!col[v] && !dfs(v,3 - c)) return false;
}
return true;
};
// rep(i,1,n) cout << col[i] << " \n"[i == n];
for(int i = 1;i <= n;i ++) {
if(col[i] == 0) {
if(!dfs(i,1)) {
cout << "NO\n";
return;
}
}
}
cout << "YES\n";
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}
F
题意
两个可重集合 \(a,b\)。我们希望对 \(b\) 中数字做如下操作,使得其变为 \(a\)
- \(op1\) :\(x := 2x\)
- \(op2\) :\(x:= \lfloor \frac{x}{2} \rfloor\)
问可行性
思路
我们可以认为,如果两个数字仅差 二的某次幂 那么这两个数字可以认为是 本质一样的。因此直接把它们所有的因子 \(2\) 先给扬了。
现在剩下一堆奇数。如果 \(b\) 中一个数字不能和 \(a\) 的某个数字匹配,就不断除二。
此时应该贪心的一找到可匹配数字就进行匹配。
这样匹配还有一个问题,比如 15 可以通过除二和 7 ,3 匹配,那么如果 \(a\) 中同时有 7,3 我们该如何安排匹配呢。
也就是说会不会存在 \(x\) 使得产生 \((15,3),(x,7)\) 的匹配使得答案由不可行变为可行。答案是否定的。
因为如果存在,显然 \(x \ge 15\)
当 \(x = 15\) ,贪心解仍符合。
当 \(x>15\),如果存在一个比 \(15\) 大的待匹配数字,我们只能使用匹配能力更强的 \(x\) ,这符合贪心策略。如果不存在,贪心解也不会使答案变差。
综上,遇到就匹配就行。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int n; cin >> n;
map<int,int> mp;
for(int i = 1;i <= n;i ++) {
int t; cin >> t;
while(t % 2 == 0) t /= 2;
mp[t] += 1;
}
bool ok = 1;
for(int i = 1;i <= n;i ++) {
int t; cin >> t;
while(t % 2 == 0) t /= 2;
while((mp.count(t) == 0 or mp[t] == 0) and t > 0) t /= 2;
if (t > 0) mp[t] -= 1;
else ok = 0;
}
cout << (ok ? "YES\n" : "NO\n");
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}
G12
题意
给一棵树,\(q\) 次询问,每次给一堆关键点,问是否有一条简单路径,经过所有关键点。
思路
对G1,\(O(qn)\) 就可以过。因此就是一个分类讨论的事情。
我们发现,当一个子树的儿子中超过2个有关键点就寄了。如果有两个儿子,并且在这个子树外面再有一个点也寄了。我们搜索判断就行。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int n; cin >> n;
vector<vector<int>> adj(n + 1);
for(int i = 1;i <= n - 1;i ++) {
int u,v; cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
// cout << "YES";return;
int q; cin >> q;
while(q --) {
vector<int> col(n + 1),cnt(n + 1);
int m; cin >> m;
for(int i = 0;i < m;i ++){
int x; cin >> x;
col[x] = 1;
}
bool flg0 = 0,flg1 = 0;
function<void(int,int)> dfs = [&](int u,int fa) {
int ks = 0;
for(int v : adj[u]) if(v != fa) {
dfs(v,u);
if(cnt[v] > 0) ks += 1;
cnt[u] += cnt[v];
}
// cout << u << " " << ks << endl;
bool innow = 0;
if(ks > 2) {
flg0 = flg1 = 1;
}else if(ks == 2) {
if(flg0 == 0) flg0 = 1,innow = 1;
else flg1 = 1;
}else if(ks == 1) {
}else {
}
if(col[u] and flg0 and not innow) flg1 = 1;
cnt[u] += col[u];
};
dfs(1,0);
if(flg1) cout << "NO\n";
else cout << "YES\n";
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
// int T;cin>>T;
// while(T--)
solve();
return 0;
}
对G2,\(q\) 很大,需要预处理。我们在总结一下G2的结论。
其实就是如果两个点简单路径外还有一个点就寄了。
两点的简单路径长度可以用lca求。
然后翻译一下这个条件和路径长度的关系,其实就是如果 \(A->B\) 的路径长度不等于 \(A->C->B\) 的路径长度,并且我们不用枚举 \(A,B\) 。因为它们可以取成深度最深点,和离他最远的点。可以想到所有的 \(C\) 都应该在这条简单路径上。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int n; cin >> n;
vector<vector<int>> adj(n + 1);
for(int i = 1;i <= n - 1;i += 1) {
int u,v; cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<int> dep(n + 1);
vector<vector<int>> par(n + 1,vector<int>(21));
function<void(int,int)> dfs0 = [&](int u,int fa) {
dep[u] = dep[fa] + 1;
par[u][0] = fa;
for(int j = 1;(1 << j) < dep[u];j += 1) {
par[u][j] = par[par[u][j - 1]][j - 1];
}
for(auto v : adj[u]) if(v != fa) dfs0(v,u);
};
dfs0(1,0);
auto lca = [&](int x,int y) {
if(dep[x] < dep[y]) swap(x,y);
for(int i = 20;i >= 0;i -= 1) {
if(dep[par[x][i]] >= dep[y]) {
x = par[x][i];
}
}
if(x == y) return x;
for(int i = 20;i >= 0;i -= 1) {
if(par[x][i] != par[y][i]) {
x = par[x][i];
y = par[y][i];
}
}
return par[x][0];
};
int q; cin >> q;
while(q --) {
auto dis = [&](int x,int y) {
return dep[x] + dep[y] - 2 * dep[lca(x,y)];
};
int m; cin >> m;
int beg = 0,mxd = 0;
vector<int> s(m + 1);
for(int i = 0;i < m;i ++) {
cin >> s[i + 1];
if(dep[s[i + 1]] > mxd) {
mxd = dep[s[i + 1]];
beg = s[i + 1];
}
}
int end = 0,mxdis = -1;
for(int i = 1;i <= m;i += 1) {
if(dis(beg,s[i]) > mxdis) {
mxdis = dis(beg,s[i]);
end = s[i];
}
}
bool ok = 1;
for(int i = 1;i <= m;i ++) {
if(dis(beg,s[i]) + dis(s[i],end) != mxdis) {
ok = 0;
break;
}
}
cout << (ok ? "YES" : "NO") << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//int T;cin>>T;
//while(T--)
solve();
return 0;
}
差1题ak,但G2也不难,第二天一早就过了。天太晚,脑子越来越糊涂。