Codeforces Round #646 (Div. 2)【ABCDE】(题解)
涵盖知识点:树上dp、树上博弈、二分
比赛链接:传送门
A - Odd Selection
题意: \(n\)张牌中选\(k\)张使得和为奇数
题解: 奇偶判断直接上代码。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while(t--){
int n,x;
int odd=0,even=0;
cin>>n>>x;
for(int i=0,v;i<n;i++){
cin>>v;
if(v&1)odd++;
else even++;
}
if(odd&&even){
if(n==x){
if(odd&1)puts("YES");
else puts("NO");
}
else puts("YES");
}
else{
if((x&1)&&odd)puts("YES");
else puts("NO");
}
}
return 0;
}
B - Subsequence Hate
题意: 给定\(01\)串,问最少改变几个字符可以使得不存在\(010\)或\(101\)的子序列。
题解: 利用前缀和顺序扫描一遍。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
char s[1010];
int pre[1010];
const int inf=0x3f3f3f3f;
int main(){
int t;cin>>t;
while(t--){
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;i++)pre[i]=pre[i-1]+(s[i]=='1');
int ans=inf;
for(int i=0;i<=n;i++){
ans=min(ans,pre[i]+(n-i-(pre[n]-pre[i])));
ans=min(ans,i-pre[i]+pre[n]-pre[i]);
}
cout<<ans<<"\n";
}
return 0;
}
C - Game On Leaves
题意: AB博弈,每次从树上取下一个叶子节点。谁先拿到树上标记为\(x\)的节点赢。
题解: 特判\(x\)为叶子节点的情况。其他情况一定会拿到只剩三个节点且\(x\)为中间节点。判断奇偶性即可。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
char s[1010];
int pre[1010];
const int inf=0x3f3f3f3f;
int main(){
int t;cin>>t;
while(t--){
int n,x;
cin>>n>>x;
int deg=0;
for(int i=1,u,v;i<=n-1;i++){
cin>>u>>v;
if(u==x||v==x)deg++;
}
if(deg<=1)puts("Ayush");
else{
if(n&1)puts("Ashish");
else puts("Ayush");
}
}
return 0;
}
D - Guess The Maximums
题意: 互动。有一个长度为\(n\)(1000以内)的数组\(a\)(未知),长度为\(k\)的不等长二维子集数组\(s\)(给出),且这\(k\)个数组完全不存在相同元素。所求密码的长度同样为\(k\)。现规定每位密码的值为\(a\)中的下标不在对应\(s\)中出现的最大值。形式上,\((S_i \underset{i \neq j} \cap S_j = \emptyset)\),\(P_i = \max\limits_{j \notin S_i} A[j]\)。现在每次询问可以给出一系列的下标,我们会得到所查询的所有下标的数组中值的最大值。要求在\(12\)次询问后得到正确的密码
题解: 既然所有\(s\)独立,那么最多就只存在一个位置的密码不是\(a\)的最大值。(有可能全是最大值,因为\(s\)中可能不含最大值对应的下标。)所以我们先花\(1\)次来查询\(n\)个位置的最大值。然后二分搜索最大值所出现的位置。这里最多花费10次(\(2^{10}=1024\))。最后一次我们用来查询这个位置的最大值就行了。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef vector<int> vi;
int query(vi q){
cout<<"? "<<q.size()<<" ";
for(auto i:q){
cout<<i<<" ";
}
cout<<"\n";
cout.flush();
int x;
cin>>x;
return x;
}
int main(){
int t;cin>>t;
while(t--){
int n,k;
cin>>n>>k;
vector<vi> s(k);
vi ans(k);
for(int i=0,c;i<k;i++){
cin>>c;
s[i].resize(c);
for(int j=0;j<c;j++)cin>>s[i][j];
}
vi q;
for(int i=1;i<=n;i++)q.push_back(i);
int mx=query(q);
int l=0,r=k-1;
while(l<r){
int mid=(l+r)/2;
q.clear();
for(int i=0;i<=mid;i++){
for(auto j:s[i])q.push_back(j);
}
int x=query(q);
if(x==mx)r=mid;
else l=mid+1;
}
q.clear();
vi vis(n+1);
for(auto i:s[l])vis[i]=1;
for(int i=1;i<=n;i++){
if(!vis[i])q.push_back(i);
}
for(int i=0;i<k;i++){
if(i==l)ans[i]=query(q);
else ans[i]=mx;
}
cout<<"! ";
for(auto i:ans){
cout<<i<<" ";
}
cout<<"\n";
cout.flush();
string correct;
cin>>correct;
}
return 0;
}
E - Tree Shuffling
题意: \(n\)结点的树编号为\(1到n\),根为\(1\)。每个节点的选取代价、当前值、目标值分别为\(a_i,b_i,c_i\)。当前值和目标值为\(01\)值。每次操作可以选取一个结点\(u\),在该节点的子树中任意选取\(k\)个结点重拍这些节点里面的数字,花费\(k\times a_u\)。求所有节点是否可达目标并求最小花费。
题解: 首先处理一下数组\(a\)。它的值是他的所有可能祖先节点中的最小值。然后计算每一棵子树所需要交换的数量。多出来的丢给父亲节点来处理即可。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef vector<int> vi;
const int maxn=2e5+10;
vi edg[maxn];
int a[maxn],b[maxn],c[maxn];
typedef long long ll;
ll ans=0;
int dp[maxn][2];
void dfs(int u,int p){
if(p)a[u]=min(a[u],a[p]);
for(auto v:edg[u]){
if(v!=p){
dfs(v,u);
dp[u][0]+=dp[v][0];
dp[u][1]+=dp[v][1];
}
}
if(b[u]!=c[u])dp[u][b[u]]++;
int mn=min(dp[u][0],dp[u][1]);
ans+=2ll*mn*a[u];
dp[u][0]-=mn;
dp[u][1]-=mn;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i]>>b[i]>>c[i];
for(int i=1,u,v;i<=n-1;i++){
cin>>u>>v;
edg[u].push_back(v);
edg[v].push_back(u);
}
dfs(1,0);
if(dp[1][0]||dp[1][1])cout<<-1<<"\n";
else cout<<ans<<"\n";
return 0;
}