返回顶部

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;
}

posted @ 2020-06-01 16:46  Charles1999  阅读(355)  评论(0编辑  收藏  举报