Codeforces Round 881 (Div.3) 萌新题解(A-E)

本文给出了五个编程问题的解决方案,涉及数组涂色以最大化得分、通过翻转区间求元素和最大值及操作次数、满二叉树路径节点和、树的子树叶子节点数量查询以及区间内1的数量超过0的最少操作步数。每个问题都提供了思路分析和C++代码实现,主要考察了数据结构和算法的应用技巧。
摘要由CSDN通过智能技术生成

A.题意:给一数组涂色,有无数种颜色可涂,规定数组的得分为每种颜色的数字的最大值和最小值的差之和,问该数组的最大得分
思路:可以直接将数组排序,然后最大元素和最小元素涂同一种颜色,次大元素和次小元素涂同一颜色,以此类推
代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)

typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int a[200200];
void solve() {
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    sort(a+1,a+n+1);
    int ans=0;
    for(int i=1;i<=n/2;i++)
    {
        ans+=a[n+1-i]-a[i];
    }
    cout<<ans<<endl;
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}

B.题意:
给定一个数组,每次操作可以将一段连续的区间的数字变成其相反数,问若干次操作后数组元素之和的最大值以及最少的操作次数
思路:

思路:
显然最大值是所有元素绝对值之和
对于最少操作次数,考虑贪心
对于连续负数段,必须进行操作
而遇到不为零的非负数(也就是正数),不在需要操作区间的范围内

代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)

typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int a[200200];
void solve() {
    int n;
    cin>>n;
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum+=abs(a[i]);
    }
    int cnt=0;
    bool flag=false;
    for(int i=1;i<=n;i++)
    {
        if(a[i]<0&&!flag)
        {
            cnt++;
            flag=true;
        }
        if(a[i]>0)
        {
            flag= false;
        }
    }
    cout<<sum<<" "<<cnt<<endl;
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}

C.题意:给一颗满二叉树,问从根节点1到达目标位置n的路径的节点总和为?

思路:
从n暴力向1跳即可

代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)

typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

void solve() {
    int n;
    cin>>n;
    int ans=0;
    while(n)
    {
        ans+=n;
        if(n%2==0)
        {
            n/=2;
        }
        else{
            n--;
            n/=2;
        }
    }
    cout<<ans<<endl;
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}

D.
题意:给定一颗树,给定q次询问,每次询问给定两个苹果的位置,苹果会沿着当前节点的孩子节点一直到叶子节点,询问苹果可能的下落情况

思路:考虑树形dp
设dp[u]为以u为根节点的字数的叶子节点的数量
通过一遍dfs计算出所有的dp值,然后对于每次询问,根据乘法原理,答案为dp[x]*dpy

代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)

typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int dp[200200];
vector <int> mp[200200];
void dfs(int u,int fa)
{
    if(mp[u].size()==1)
    {
        dp[u]=1;
    }
    for(auto v: mp[u])
    {
        if(v==fa) {
            continue;
        }
        dfs(v,u);
        dp[u]+=dp[v];
    }
}
void solve() {
    int n,m;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        mp[i].clear();
        dp[i]=0;
    }
    mp[1].push_back(0);
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        mp[u].push_back(v);
        mp[v].push_back(u);
    }
    dfs(1,0);

    cin>>m;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        cout<<dp[x]*dp[y]<<endl;
    }
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}

E.题意:给定m个不同的区间,(区间均为[1,n]子集)q次修改,初始时[1,n]区间所有元素都是0,每次修改可以将指定位置的0改成1(修改操作是有先后次序的),求至少到第几次操作时,才能使得m个区间中至少有1个区间满足:区间内1的数量严格大于0的数量,若无解输出-1

思路:假设第k次操作可行,那么再进行后面的操作也必然可行,故考虑二分答案,check函数中,先计算前缀和,这样在判断m个区间中是否有合法区间时可以做到O(1)查询,check函数复杂度O(n+m)
总的时间复杂度O((n+m)logq)

代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)

typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int l[200200];
int r[200200];
int a[200200];

int n,m;
bool check(int mid)
{
    vector <int> vis(n+1,0),sum(n+1,0);
    for(int i=1;i<=mid;i++)
    {
        vis[a[i]]=1;
    }
     for(int i=1;i<=n;i++)
     {
         sum[i]=sum[i-1]+vis[i];
     }
     for(int i=1;i<=m;i++)
     {
         if(sum[r[i]]-sum[l[i]-1]>=(r[i]-l[i]+1)/2+1)return true;
     }
    return false;
}
void solve() {
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>l[i]>>r[i];
    }
    int q;
    cin>>q;
    for(int i=1;i<=q;i++)
    {
        cin>>a[i];
    }
    int lf=1ll,ri=q;
    while(lf<ri)
    {
        int mid=(lf+ri)/2;
        if(check(mid))ri=mid;
        else lf=mid+1;
    }
    if(check(lf))cout<<lf<<endl;
    else cout<<-1<<endl;
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值