Codeforces Round #700 (Div. 2) A ~ E ,6题全,超高质量良心题解【每日亿题】2021/2/8

整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


目录

    • A - Yet Another String Game
    • B - The Great Hero
    • C. Searching Local Minimum
    • D1 - Painting the Array I
    • D2 - Painting the Array II
    • E - Continuous City

这场打的头疼,先更一下C题吧,其他的几道题明天醒了再更…

现在脑子不太清醒,先口胡一个最简单的C吧…

来了来了,更一下 A ~ E

实际上是昨天快两点才睡着,快九点了才起床,十点才吃完饭开始写

比赛链接:https://codeforces.com/contest/1480

A - Yet Another String Game

Problem 1480A - Yet Another String Game

Alice和 Bob 在玩游戏,Alice 先手,给定一个字符串,他们每次可以进行一个操作:让任意一个没有被选过的位置上的字符变成另一个不一样的字符。Alice 想让字符串最后的字典序最小,Bob 想让字符串最后的字典序最大,两位都会进行最优的操作,请问最后字符串会变成什么鬼样子 ~

Solution

签到题

模拟一下就行了,两位都尽量朝着自己的方向去选,所以Alice先选一定选第一个,然后 Bob 会选第二个,为了使得序列的字典序尽可能的打,所以就是奇偶交替进行…

#include 
#include 
#include 
#include 
#include 

using namespace std;

const int N = 50007;

int n, m, t;
string a, b;

int main()
{
     
    scanf("%d", &t);
    while(t -- ) {
     
        cin >> a;
        int len = a.length();
        int cnt = 0, cnt1 = len, num = 0;
        for(int i = 0; i < len; ++ i){
     
            if(!(i & 1)) {
     
                if(a[i] == 'a') a[i] = 'b';
                else a[i] = 'a';
            }
            else {
     
                if(a[i] == 'z') a[i] = 'y';
                else a[i] = 'z';
            }
        }
        cout << a <<  endl;
    }
    return 0;
}

B - The Great Hero

Problem 1480B - The Great Hero

一个简单的贪心。开始还以为是一个模拟,然后立马发现,因为可以同归于尽,所以我们最后一击要用到正确的地方。因为我们打的顺序无所谓,所以可能存在一种情况,我随便打,中间死了,但是我把攻击力最大的哪个怪,放到最后跟他同归于尽,就会最后死,答案从 NO 变成了 YES

然后再来分析这么解决。

首先很明显,对于第 i i i 只怪,我们需要 ⌈ b i A ⌉ \lceil \frac{b_i}{A} \rceil Abi 次才能打死它,然后每次会掉 a i a_i ai 的血,如果直接打死它的话,会掉 ⌈ b i A ⌉ × a i \lceil \frac{b_i}{A} \rceil\times a_i Abi×ai 的血。

所以我们就先模拟一遍,打一遍,然后最后的血量加上攻击力最大的那只怪的攻击力,相当于就是把这只放到最后打,要是加上之后的血量大于0,说明成功,否则中间就死了…

因为我特判的是 >0 而不是 >=0 ,我先扣掉了所有的血,再加上最大攻击力,如果我这时候剩余的血量 >0 说明我活到了最后一只攻击力最大的怪 例如这个

1
10 10 2
2 1000
50 10

答案就是NO,我的代码也是NO,我没办法活到1000的那个怪 ~

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PLL;
typedef int itn;
const int N = 5e5 + 7, M = 1e6 + 7, mod = 1e9 + 7;
const ll INF = 1e18 + 7;
ll n, m, t;
ll A, B;
PLL a[N];
bool solve()
{
     
    scanf("%lld%lld%lld", &A, &B, &n);
    ll maxx = -INF;
    for(int i = 1; i <= n; ++ i) {
     
        scanf("%lld", &a[i].x);
        maxx = max(maxx, a[i].x);
    }
    for(int i = 1; i <= n; ++ i) {
     
        scanf("%lld", &a[i].y);
    }

    ll sum = 0;
    for(int i = 1; i <= n; ++ i) {
     
        ll cnt = ceil(a[i].y * 1.0 / A);
        sum += cnt * a[i].x;
    }
    B -= sum;
    B += maxx;
    if(B > 0) return true;
    else return false;
}

int main()
{
     
    scanf("%lld", &t);
    while(t -- ) {
     
        bool ans = solve();
        if(ans) puts("YES");
        else puts("NO");
    }
    return 0;
}

C. Searching Local Minimum

Problem 1479A - Searching Local Minimum

交互题,有一个 1 1 1 ~ n n n 的一个排列,只会给你 n n n 的值,求一个为 V 字形的数的下标。其中 V 字形数是指下标为 i i i 的一个数, a i − 1 ≥ a i ≤ a i + 1 a_{i-1}\ge a_i\le a_{i+1} ai1aiai+1 ,想让你找到的这个 i i i。你每次可以询问这个排列的一个下标的值,你最多可以问 100次。

1 ≤ n ≤ 1 0 5 1\le n \le 10^5 1n105

Solution

因为题目要找的是一个 v 字形的数,也就是 a i − 1 ≥ a i ≤ a i + 1 a_{i-1}\ge a_i\le a_{i+1} ai1aiai+1 的这个 i i i

数据 1 0 5 10^5 105

如果 a i ≤ a i + 1 a_i\le a_{i+1} aiai+1 那么 一定 a i ≥ a i − 1 a_i\ge a_{i-1} aiai1 ,不然 i i i 就是答案。同理, a i ≥ a i − 1 a_i\ge a_{i-1} aiai1,那么 a i − 1 ≥ a i − 2 a_{i-1}\ge a_{i-2} ai1ai2 ,一次类推,一定是一个单调的

同理若 a i ≥ a i + 1 a_i\ge a_{i+1} aiai+1 也一样单调,所以二分就行了,若 a[mid] 大于右边,则答案在左边…

没了,睡觉睡觉

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef int itn;
const int N = 5e3 + 7, M = 1e6 + 7, mod = 1e9 + 7;
const ll INF = 1e18 + 7;

int n, m, t, k, q;
int a[N];
int vis[M];
PII ans[N];

int ask(int x)
{
     
    printf("? %d\n", x);
    fflush(stdout);
    int res;
    scanf("%d", &res);
    return res;
}

void solve()
{
     
    int l = 1, r = n;
    while(l < r) {
     
        int mid = l + r >> 1;
        int rr = ask(mid + 1);
        int midd = ask(mid);
        if(midd < rr) r = mid;
        else l = mid + 1;
    }
    printf("! %d\n", l);
    fflush(stdout);
    return ;
}

int main()
{
     
    scanf("%d", &n);
    solve();
    return 0;
}

D1 - Painting the Array I

Problem 1479B1 - Painting the Array I

给你一个序列 a a a,请你将这个序列撕开分成两个序列 a ( 0 ) a^{(0)} a(0) a ( 1 ) a^{(1)} a(1),使得将 a ( 0 ) a^{(0)} a(0) a ( 1 ) a^{(1)} a(1) 合并所有相邻且相同的元素之后,两个序列剩余的元素个数和最大。

例如:

1 2 3 1
1 2 3 2

答案均为4。

Solution

我们发现直接遇见两个一样的就直接加2什么的暴力算的话不一定对,因为后面可能会出现多算的情况。就是你以为分开就好了,可以加,但是它却和放到的新序列的前一个元素重复,这样就多加了。

例如:

4 4 2 4 4 

答案应该是 4 而不是 5 。

所以要考虑贪心,分类讨论后面可能会发生的情况。

我们一步一步分析。

我们首先设 a ( 0 ) a^{(0)} a(0) 序列的最后一个元素为 x x x a ( 1 ) a^{(1)} a(1) 的最后一个元素为 y y y

分类讨论,我们分别考虑什么情况的时候,把当前元素分配给哪一个序列会更优。

对于当前准备去分配的元素 z 1 z1 z1,以及 z 1 z1 z1 后面一位元素 z 2 z2 z2

(1) 考虑对前面的贡献

  1. x == y 时,上下两个序列都无所谓。
  2. x == z1 && y != z1 时,很明显分配给 y y y 会更优。
  3. y == z1 && x != z1 时,很明显分配给 x x x 会更优。

(2) 考虑对后面的贡献

  1. x == z2 && y != z2 时,很明显分配给 x x x 会更优,因为可能 z 1 z1 z1 可以填上这个合并的漏洞,填不上的话,就算了,但会更优
  2. y == z2 && x != z2 时,同理很明显分配给 y y y 会更优。
  3. x == y == z2 那就无所谓了 ~ 给谁都行

剩余的所有情况应该像D2那样去判断与 x x x y y y 相同的元素的距离谁最近,就放到谁后面。具体思路 / 证明看下面的 D2 的题解,这里可能是因为数据较弱,所以过掉了,然后我也懒得改了 ~ (往下滑就看到了)

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
typedef int itn;
const int N = 5e6 + 7, M = 6e6 + 7, mod = 1e9 + 7;
const ll INF = 1e18 + 7;

int n, m, t, k;
itn a[N];
int vis[N];
void solve()
{
     
    int ans = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i) {
     
        scanf("%d", &a[i]);
    }
    int x = -1, y = -1;
    for(int i = 1; i <= n; ++ i) {
     
        itn z1 = a[i], z2 = a[i + 1];
        if(z1 == x && z1 == y) continue;
        if(z1 == x) {
     
            y = z1;
            ans ++ ;
        }
        else if(z1 == y) {
     
            x = z1;
            ans ++ ;
        }
        else {
     
            ans ++ ;
            if(z2 == x && z2 != y) {
     
                x = z1;
            }
            else if(z2 == y && z2 != x) {
     
                y = z1;
            }
            else x = z1;
        }
    }
    printf("%d\n", ans);
}

int main()
{
     
    solve();
    return 0;
}


D2 - Painting the Array II

Problem 1479B2 - Painting the Array II

给你一个序列 a a a,请你将这个序列撕开分成两个序列 a ( 0 ) a^{(0)} a(0) a ( 1 ) a^{(1)} a(1),使得将 a ( 0 ) a^{(0)} a(0) a ( 1 ) a^{(1)} a(1) 合并所有相邻且相同的元素之后,两个序列剩余的元素个数和最小。

Solution

我们先按照上面 D1 的贪心策略分析。

我们首先设 a ( 0 ) a^{(0)} a(0) 序列的最后一个元素为 x x x a ( 1 ) a^{(1)} a(1) 的最后一个元素为 y y y

分类讨论,我们分别考虑什么情况的时候,把当前元素分配给哪一个序列会更优,使得序列最短。

对于当前准备去分配的元素 z 1 z1 z1,以及 z 1 z1 z1 后面一位元素 z 2 z2 z2

(1) 首先考虑对前面的贡献

  1. x == y 时,上下两个序列给谁都无所谓
  2. x == z1 && y != z1 时,很明显分配给 x x x 会更优。
  3. y == z1 && x != z1 时,很明显分配给 y y y 会更优。

(2) 若上述条件均为达到就考虑对后面的贡献

  1. x == z2 && y != z2 时,很明显分配给 y y y 会更优
  2. y == z2 && x != z2 时,同理很明显分配给 x x x 会更优

最后一种情况:若x != z2 && y != z2,以及其他的所有剩余情况,这时候就有讲究了。

看上去放到哪里都区别不大,但是我们想要最终的答案尽可能地小,也就是让元素尽可能合并,也就是:

a [ i ] a[i] a[i] 以后和 x x x 相同的元素(假设是 x x xx xx)尽量能和 x x x 合并,也就是以后 x x x 后面都不添加数。

a [ i ] a[i] a[i] 以后和 y y y 相同的元素(假设是 y y yy yy)尽量能和 y y y 合并, 也就是以后 y y y 后面都不添加数。

但是我们总归是要在 x x x 或者 y y y 后面选择一个数放进去,假设我们放到了 x x x 后面,这样也就断绝了后面的那个与 x x x 相同的元素 x x xx xx x x x 合并的可能性。

所以我们应该取两个队列末尾元素: x x x y y y与它们相同的数 x x xx xx 或者 y y yy yy 的距离更近的那个队列

我们假设是 y y y ,这样与 y y y 相同的数 y y yy yy 比与 x x x 相同的数 x x xx xx 距离更近,也就是一个一个来放的话,更先到达两个队列面前,是距离更短的那个数,也就意味着最终可以和 y y y 合并的可能性就更大,因此我们就把 a [ i ] a[i] a[i] 放到 x x x 的后面,让 y y y 去追逐合并的梦想 ~

应该很好理解,非常形象。

我们预处理一下下标 i i i 的最近的下一个相同元素的下标 n e x [ i ] \tt nex[i] nex[i] 就行了,如何实现具体看代码,很好理解。

总结一下就是:

x != z2 && y != z2,以及其他的所有剩余情况时:若nex[x] < nex[y] ,我们分配给 y y y 更优

x != z2 && y != z2,以及其他的所有剩余情况时:若nex[x] > nex[y] ,我们分配给 x x x 更优


所有条件按照我分析的时候的先后顺序if else 判断即可,因为越往前优先级越大,后面只是有合并的可能性,而前面是直接已经可以合并了。

最后简单实现一下就行了

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
typedef int itn;
const int N = 5e5 + 7, M = 6e6 + 7, mod = 1e9 + 7;
const ll INF = 1e18 + 7;

int n, m, t, k;
itn a[N], b[N];
vector<int> v[N];
int nex[N];

void solve()
{
     
    int ans = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i) {
     
        scanf("%d", &a[i]);
        v[a[i]].push_back(i);
    }
    for(int i = 1; i <= n; ++ i)
        nex[i] = n + 1;

    for(int i = 1; i <= n; ++ i) {
     
        for(int j = 0; j < (int)v[i].size() - 1; ++ j) {
     
            nex[v[i][j]] = v[i][j + 1];
        }
    }
    int x = 0, y = 0, nex_x = n + 1, nex_y = n + 1;
    for(int i = 1; i <= n; ++ i) {
     
        int z1 = a[i], z2 = a[i + 1];
        if(z1 == x) {
     
            nex_x = nex[i];
        }
        else if(z1 == y) {
     
            nex_y = nex[i];
        }
        else {
     
            ans ++ ;
            if(z2 == x && z2 != y) {
     
                y = z1;
                nex_y = nex[i];
            }
            else if(z2 == y && z2 != x) {
     
                x = z1;
                nex_x = nex[i];
            }
            else {
     
                if(nex_x < nex_y) {
     
                    y = z1;
                    nex_y = nex[i];
                }
                else {
     
                    x = z1;
                    nex_x = nex[i];
                }
            }
        }
    }
    printf("%d\n", ans);
}

int main()
{
     
    solve();
    return 0;
}

E - Continuous City

Problem 1479C - Continuous City

定义中心图为:

n n n 个点,编号为 1 1 1 ~ n n n 。以及 m m m 条有向边。每一条有向边上都有一个正整数权值,并且这 m m m 条路都是从编号小的指向编号大的(意味着每次走都会朝着中心 n n n 进发)。并且对于任意两个不同的点,他们之间最多只有一条有向边。

定义 ( L , R ) (L,R) (L,R) 连续图为既满足中心图,又满足下述两个条件的有向图:

  1. 所有的从 1 1 1 n n n 的简单路径的长度 l e n ∈ [ L , R ] len\in[L,R] len[L,R]
  2. 对于任意的 d d d L ≤ d ≤ R L\le d\le R LdR,均恰好存在一条且仅有一条从 1 1 1 n n n 的简单路径的长度等于 d d d

仅给定 L L L R R R,请问你能否构造一个符合题意且点数 n n n 不超过 32 的 ( L , R ) (L,R) (L,R) 连续图,如果不能,输出 NO ,否则输出 YES 并输出该图的 n n n m m m (点数与边数),并输出 m m m 条边的情况(对于每条边,输出 x , y , z x,y,z x,y,z 表示第 i i i 条边从 x x x y y y 边长为 z z z

Solution

待更…

你可能感兴趣的:(Codeforces,比赛题解)