返回顶部

Codeforces Round #660 (Div. 2)【ABCD】(题解)

涵盖知识点:思维、dfs

比赛链接:传送门

A - Captain Flint and Crew Recruitment

题意: 构造\(a,b,c,d\)满足\(a+b+c+d=n\)且其中至少三个数字为两质数的积。
题解: 找到最小的三个两质数积为\(6,10,14\)。那么小于这三个数字的和的值均无法达成条件。大于的都可以,注意剩下的值和这三个值相等的条件特判一下即可。
Accept Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    int t;cin>>t;
    while(t--){
        int n;
        cin>>n;
        if(n<=30)puts("NO");
        else{//6 10 14
            puts("YES");
            if(n==36)puts("5 6 10 15");
            else if(n==40)puts("6 9 10 15");
            else if(n==44)puts("6 9 14 15");
            else{
                printf("6 10 14 %d\n",n-30);
            }
        }
    }
}

B - Captain Flint and a Long Voyage

题意: 构造一个\(n\)位的十进制数,将其每一位转化成二进制后删除最后的\(n\)位。要求值尽可能大且构造的十进制数尽可能小。
题解: 只有\(8,9\)的二进制是\(4\)位,而这两个数字在被删除位数的情况下是等价的。所以末尾用\(8\),开头用\(9\)就行了。
Accept Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    int t;cin>>t;
    while(t--){
        int n;
        cin>>n;
        int cnt=(n+3)/4;
        for(int i=0;i<n-cnt;i++)cout<<9;
        for(int i=0;i<cnt;i++)cout<<8;
        cout<<"\n";
    }
}

C - Uncle Bogdan and Country Happiness

题意: 给定一棵树,初始每个人都在根,然后他们会沿着最短路径走向各自最终的目标节点(未知),但我们直到每个节点是多少人的目标。每个人在初始的时候心情有好有坏,但是在路上的时候好心情会变坏,但是坏心情不会变好。现在给定了每个节点的数据为好心情人数-坏心情人数。问是否存在一种安排使得所有数据合理。
题解: 通过dfs扫描一遍子节点的人数,如果存在某个节点的数据大于所有子节点的人数的情况,那么就一定不存在这组解。假设初始状态下所有人到达最后结点的数据都是坏心情,那么通过父节点的数据一定不能小于所有子节点的坏心情人数之和取相反数。由于每个节点发生的数据变化都是一增一减,所以理想值和真实值的差值一定为\(2\)的倍数。全局扫描一遍就可以了。
Accept Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
vector<int> edg[maxn];
ll p[maxn],h[maxn],num[maxn];
bool check;
void dfs(int u,int pre){
    for(auto v:edg[u]){
        if(v==pre)continue;
        dfs(v,u);
        num[u]+=num[v];
        p[u]+=p[v];
    }
    if(h[u]>num[u]||h[u]<p[u])check=false;
    else if((h[u]-p[u])%2)check=false;
    p[u]=h[u];
}
int main(){
    int t;
    cin>>t;
    while(t--){
        check=true;
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++)edg[i].clear();
        for(int i=1;i<=n;i++){
            cin>>p[i];
            num[i]=p[i];
            p[i]=-p[i];
        }
        for(int i=1;i<=n;i++)cin>>h[i];
        for(int i=1,u,v;i<n;i++){
            cin>>u>>v;
            edg[u].push_back(v);
            edg[v].push_back(u);
        }
        dfs(1,1);
        puts(check?"YES":"NO");
    }
    return 0;
}

D - Captain Flint and Treasure

题意: 给定两个数组\(a,b\)。对于每次操作选择一个下标\(i\),将\(a_i\)计入贡献,同时若\(b_i\neq-1\),则\(a_{b_i}=a_{b_i}+a_i\)。每个下标必须取且只能取一次。求最终贡献的最大值。
题解: 题目限定了条件数组\(b\)的值不会成环,那么这就是一个网络,我们对其进行拓扑排序,然后从原点开始,如果对应的值为负数,那么我们一定留在最后取并且按照拓扑排序的逆序进行选择,以免对后续产生负收益。正值一定会产生正收益,所以按照拓扑排序的结果优先取。
Accept Code:

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
ll a[maxn],b[maxn],deg[maxn];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++) {
        cin >> b[i];
        if(b[i]!=-1)deg[b[i]]++;
    }
    ll ans=0;
    queue<ll> q;
    for(int i=1;i<=n;i++)if(deg[i]==0)q.push(i);
    vector<ll> ans1,ans2;
    while(!q.empty()){
        ll tmp=q.front();
        q.pop();
        if(b[tmp]!=-1){
            deg[b[tmp]]--;
            if(deg[b[tmp]]==0)q.push(b[tmp]);
            if(a[tmp]>0)a[b[tmp]]+=a[tmp];
        }
        ans+=a[tmp];
        if(a[tmp]>=0)ans1.push_back(tmp);
        else ans2.push_back(tmp);
    }
    reverse(ans2.begin(),ans2.end());
    cout<<ans<<"\n";
    for(auto i:ans1)cout<<i<<" ";
    for(auto i:ans2)cout<<i<<" ";
    cout<<"\n";
    return 0;
}
posted @ 2020-08-04 17:29  Charles1999  阅读(105)  评论(0编辑  收藏  举报