问答媒体

 找回密码
 立即注册
快捷导航
搜索
热搜: 活动 交友 discuz
查看: 134|回复: 9

Codeforces Global Round 23 A

[复制链接]

3

主题

6

帖子

14

积分

新手上路

Rank: 1

积分
14
发表于 2022-11-29 13:57:06 | 显示全部楼层 |阅读模式
A Maxmina

代码实现

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
using LL = long long;

int main(){

    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int T;
    cin >> T;
    while(T--){
        int n, k;
        cin >> n >> k;
        vector<int> a(n);
        for(auto &x : a) cin >> x;
        if (a == vector<int>(n, 0)) cout << "NO" << '\n';
        else cout << "YES" << '\n';
    }

}
B Rebellion

#include<iostream>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;
using LL = long long;
const int maxn = 1e5 + 5;
int a[maxn];

int main(){

    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int T;
    cin >> T;
    while(T--){
        int n;
        cin >> n;
        set<int> s;
        for(int i = 1; i <= n; i++){
            cin >> a;
            if (a == 0) s.insert(i);
        }
        int ans = 0;
        for(int i = 1; i <= n; i++){
            if (s.empty()) break;
            if (a == 0){
                s.erase(i);
            }
            else{
                ans++;
                s.erase(prev(s.end()));
            }
        }
        cout << ans << '\n';
    }

}
C Permutation Operations

分析

这题一开始题给读错了,以为是每次操作给每个后缀加一,想了半天没想出来咋做,后来重新读题发现每次操作是加i...
首先通过操作我们一定可以把逆序对的数量变为0.我们发现只要对于每个a_i < a_{i-1}的位置,在i处操作直至a_i = a_{i - 1}就能删除所有逆序对,所以需要操作的地方一共最多只有n处,而这些位置差的和不会超过n,所以n次操作一定可以删除所有的逆序对,给这些位置排一下序贪心的去操作即可.
代码实现

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
using LL = long long;
const int maxn = 1e5 + 5;
int a[maxn];
pair<int, int> d[maxn];

int main(){

    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int T;
    cin >> T;
    while(T--){
        int n;
        cin >> n;
        for(int i = 1; i <= n; i++){
            cin >> a;
            d = {a - a[i - 1], i};
        }
        sort(d + 1, d + n + 1);
        for(int i = n; i >= 1; i--)
            cout << d.second << " \n"[i == 1];
    }

}
D Paths on the Tree

分析

显然每条路径一定尽可能地长比较好,所以每条路径一定是根到某个叶子结点.
我们可以发现对于每个结点,经过其的路径数最多只有两种取值,且一定为k, k + 1(差不超过1)的形式,简单用归纳法证明一下.

  • 首先根节点的取值数量只有1种,为了方便证明我们也可以写成k, k + 1的形式.
  • 若某个结点的的取值为k, k + 1(1) 如果其只有一个儿子,那么儿子的取值也是k, k + 1,不超过两种(2) 若其不止一个儿子,假设儿子数量为s.当k = 0(\bmod \ s)时,儿子的取值只有两种\frac{k}{s}, \frac{k}{s} + 1,当k \ne 0(\bmod \ s)时,儿子的取值也只有两种\lfloor \frac{k}{s} \rfloor, \lfloor \frac{k}{s} \rfloor + 1.所以如果父亲的取值是k, k + 1这样形式,其儿子的取值范围一定也是k, k + 1的形式,所以树中所有结点都满足取值最多只有两种,且差值不超过1.
然后我们就可以做树形DP,对于每个儿子求出其两种取值时DP值分别是多少,设儿子j取k/k+1时DP值为f_{j, 0/1},求完后我们可以把儿子按照f_{j,1} - f_{j,0}排序,优先让f_{j,1} - f_{j,0}比较大的儿子取值取到k+1即可.总复杂度为O(n\log{n}).
代码实现

#include<iostream>
#include<cstring>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
using LL = long long;
using PLL = pair<LL, LL>;
const int maxn = 4e5 + 5;
vector<int> g[maxn];
int p[maxn], w[maxn];
LL f[maxn][2];

void dfs(int u, int p1, int p2){
    f[0] = 1LL * p1 * w;
    f[1] = 1LL * p2 * w;
    if (g.empty()) return;
    vector<PLL> v;
    for(auto j : g){
        dfs(j, p1 / g.size(), p1 / g.size() + 1);
        v.push_back({f[j][0], f[j][1]});
        f[0] += f[j][0];
        f[1] += f[j][0];
    }
    sort(v.begin(), v.end(), [&](PLL &a, PLL &b){
        return a.second - a.first > b.second - b.first;
    });

    {
        int t = p1 - p1 / g.size() * g.size();
        for(int i = 0; i < t; i++)
            f[0] += v.second - v.first;
    }
    {
        int t = p2 - p1 / g.size() * g.size();
        for(int i = 0; i < t; i++)
            f[1] += v.second - v.first;
    }
}

int main(){

    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int T;
    cin >> T;
    while(T--){
        int n, k;
        cin >> n >> k;
        for(int i = 1; i <= n; i++)
            g.clear();
        for(int i = 2; i <= n; i++){
            cin >> p;
            g[p].push_back(i);
        }
        for(int i = 1; i <= n; i++) cin >> w;
        dfs(1, k, k + 1);
        cout << f[1][0] << '\n';
    }

}
本文使用 Zhihu On VSCode 创作并发布
回复

使用道具 举报

3

主题

6

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-11-29 13:57:40 | 显示全部楼层
还能这样排序选学到了[大哭]
回复

使用道具 举报

0

主题

2

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-11-29 13:58:38 | 显示全部楼层
这个E真的卡脚...
回复

使用道具 举报

0

主题

3

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-11-29 13:59:29 | 显示全部楼层
佬,C题为什么根据差分数组降序贪心是对的呢,没想明白这里
回复

使用道具 举报

2

主题

7

帖子

11

积分

新手上路

Rank: 1

积分
11
发表于 2022-11-29 13:59:49 | 显示全部楼层
1. 首先最终的数组一定是没有逆序对的,所以我们的目标是对于每个i进行一些操作使得ai>=ai-1,显然每个位置要操作max(0,ai-ai-1)次,那我们当然希望操作数大的操作用到需要操作次数多的地方去,所以排序贪心。2. 关于为什么一定最终的数组一定不存在逆序对。因为相邻项相差大于等于n-1的最多只有一项(n,1),相邻项相差大于等于n-2的最多只有两项(n,2和n-1,1)以此类推大于等于n-x的相邻项最多只有x项,所以对于相邻项大于等于n-x的操作可以用一定可以用最大的x次去操作完,所以我们一定可以操作完所有的位置,所以最终的数组中一定不存在逆序对
回复

使用道具 举报

0

主题

4

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2022-11-29 14:00:32 | 显示全部楼层
[爱]
回复

使用道具 举报

0

主题

2

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-11-29 14:01:21 | 显示全部楼层
[捂脸]
回复

使用道具 举报

1

主题

7

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-11-29 14:01:42 | 显示全部楼层
感谢大佬的证明[爱]
回复

使用道具 举报

2

主题

4

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2022-11-29 14:01:51 | 显示全部楼层
C题,感觉每个数的后面开始加上它自身也可以做
回复

使用道具 举报

0

主题

8

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-11-29 14:02:20 | 显示全部楼层
妙啊[赞同]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver| 手机版| 小黑屋| 问答媒体

GMT+8, 2025-7-8 08:28 , Processed in 0.092800 second(s), 22 queries .

Powered by Discuz! X3.4

Copyright © 2020, LianLian.

快速回复 返回顶部 返回列表