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<<"
";
    }
    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 eq j} cap S_j = emptyset)),(P_i = maxlimits_{j otin 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<<"
";
    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<<"
";
        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 imes 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<<"
";
    else cout<<ans<<"
";
    return 0;
}