Educational Codeforces Round 76 (Rated for Div. 2)

A - Two Rival Students

题意:共n个人排一排,两个人,位于a,b,相邻位置交换至多x次,最大化abs(a-b)的值。

题解:每次交换至多+1,不能超过n-1。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t;
    scanf("%d", &t);
    while(t--) {
        int n, x, a, b;
        scanf("%d%d%d%d", &n, &x, &a, &b);
        printf("%d\n", min(n - 1, abs(a - b) + x));
    }
    return 0;
}

B - Magic Stick

题意:给一个数a,有无限次的两种操作:1、若a是偶数,则加上a的一半。2、若a>1,则减去1。问是否可以从x构造出y。

题解:有无限次减法,就看1操作能到达哪里就可以。首先1是不能动的,y<=1。其次,2可以到3,3可以到2,没办法突破到4,所以x=2或x=3时,y<=3。而4可以有4->6->9->8->12->18->27->26->39->...这样无限做下去,所以4以上必有解。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t;
    scanf("%d", &t);
    while(t--) {
        ll x, y;
        scanf("%lld%lld", &x, &y);
        bool suc = 0;
        if(x == 1)
            suc = (y <= 1);
        else if(x <= 3)
            suc = (y <= 3);
        else
            suc = 1;
        puts(suc ? "YES" : "NO");
    }
    return 0;
}

C - Dominated Subarray

题意:n(n<=2e5)个数的序列a,ai<=n,找最短的“被支配子区间”,一个子区间被支配有两个条件:1、长度>=2,2、有唯一的众数。

题解:一开始还想二分,二分长度然后验证,还写了一个验证的算法,可以均摊O(1)转移出众数(每次尺取的时候,众数至多变化1)。但是转头一想,小的区间不行大的未必不行的。比如我构造一个这样的:

1
7
2 2 4 4 2 2 4

假如一开始二分到4,则验证失败,任意一个子区间的众数都是2和4两个。但最短子区间长度是2。

后来想了一下,是不是最开始考虑的,是否是最短的两个相邻相同元素的差呢?简单证明之后发现的确是这样。

假如最短的两个相邻相同元素为下面的样子:

x,y1,y2,y3,...,yk,x

那么yi互不相等,也当然和x不相等,否则有更短的yi,yj与假设矛盾。则这个区间一定是“被支配子区间”。

但为什么最短“被支配子区间”一定是这样呢?

从x往两边扩展,长度肯定不会更短,而往内收缩,则不会有更短的“被支配子区间”,所以这样的相邻x组成的为被支配子区间的构成元素,长的可以由若干个小的组合得到。在这个算法下n=1不需要特判。注意别被卡memset。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n, a[200005];
int pre[200005];

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        memset(pre, -1, sizeof(pre[0]) * (n + 1));
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);

        int minlen = 1e9;
        for(int i = 1; i <= n; ++i) {
            if(pre[a[i]] != -1)
                minlen = min(minlen, i - pre[a[i]] + 1);
            pre[a[i]] = i;
        }
        if(minlen == 1e9)
            minlen = -1;

        printf("%d\n", solve());
    }
    return 0;
}

但是貌似t只有1000,在cf是卡不了memset的样子。

二分的错误算法,不过算是知道怎么O(1)转移众数了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n, a[200005];
int occ[200005];
int maxi[200005];

bool check(int len) {
    memset(occ, 0, sizeof(occ[0]) * (n + 1));
    memset(maxi, 0, sizeof(maxi[0]) * (n + 1));
    int maxtop = 0;
    for(int i = 1; i <= len; ++i) {
        --maxi[occ[a[i]]];
        ++occ[a[i]];
        ++maxi[occ[a[i]]];
        maxtop = max(maxtop, occ[a[i]]);
    }
    if(maxi[maxtop] == 1)
        return 1;
    else {
        for(int i = len + 1; i <= n; ++i) {
            --maxi[occ[a[i]]];
            ++occ[a[i]];
            ++maxi[occ[a[i]]];

            --maxi[occ[a[i - len]]];
            --occ[a[i - len]];
            ++maxi[occ[a[i - len]]];
            while(maxi[maxtop] == 0)
                --maxtop;
            maxtop = max(maxtop, occ[a[i]]);
            if(maxi[maxtop] == 1)
                return 1;
        }
    }
    return 0;
}

int solve() {
    int l = 2, r = n;
    //printf("l=%d r=%d\n", l, r);
    while(1) {
        int m = l + r >> 1;
        printf("m=%d\n", m);
        if(l == m) {
            if(check(l))
                return l;
            else if(check(r))
                return r;
            else
                return -1;
        }
        if(check(m))
            r = m;
        else
            l = m + 1;
    }
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        if(n == 1) {
            puts("-1");
            continue;
        }
        printf("%d\n", solve());
    }
    return 0;
}

D - Yet Another Monster Killing Problem

题意:有n个怪物,从左到右刷,每个怪攻击力ai,你有m个英雄,每个英雄有攻击力pi和耐力si。每天你放一个英雄往前刷,直到耐力消耗完/怪刷完/遇到攻击比他高的怪,他就会回城。英雄可以按任意顺序用任意次,无解输出-1。

题解:一开始想的是,肯定是个偏序关系,有用的英雄要么攻击高,要么耐力高,组成一条折线(一开始还在想凸壳),然后每一天就贪心尽可能往前走,由于英雄被筛选过,所以往前走d步要找的是si>=d的第一个英雄。找不到就-1。找得到就比较两者攻击力,攻击力不够也是-1,这样貌似变成一个RMQ问题。

所以排序筛选之后贪心,二分往前走的值,然后用ST表RMQ,是严格的O(nlogn)。

注意ST表的访问不能够越界!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

struct Hero {
    int s, p;
    bool operator<(const Hero &h)const {
        return s != h.s ? s < h.s : p < h.p;
    }
} h[200005], H[200005];
int n, hn, a[200005];
int st[200005], stop;

int id[200005];
//耐力超过i的攻击力最高的英雄的编号为id[i];
int cur;

class ST_Table {
  private:
    static const int MAXLOGN = 19;
    static const int MAXN = 200005;
    int logn[MAXN + 5];
    int f[MAXN + 5][MAXLOGN + 1];

  public:
    inline void init1() {
        logn[1] = 0;
        for(int i = 2; i <= MAXN; i++) {
            logn[i] = logn[i / 2] + 1;
        }
    }

    inline void init2(int n) {
        for(int i = 1; i <= n; i++)
            f[i][0] = a[i];
        for(int j = 1; j <= MAXLOGN; j++)
            for(int i = 1; i + (1 << j) - 1 <= n; i++)
                f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
    }

    inline int range_max(int l, int r) {
        int s = logn[r - l + 1];
        return max(f[l][s], f[r - (1 << s) + 1][s]);
    }
} ST;

bool check(int len) {
    int p = id[len];
    if(p == -1)
        return 0;
    else
        return H[p].p >= ST.range_max(cur + 1, min(n, cur + len));
}

int solve() {
    int l = 1, r = H[hn].s;
    while(1) {
        int m = l + r >> 1;
        if(l == m) {
            if(check(r))
                return r;
            else if(check(l))
                return l;
            else
                return -1;
        }
        if(check(m))
            l = m;
        else
            r = m - 1;
    }
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    ST.init1();
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);

        ST.init2(n);
        scanf("%d", &hn);
        for(int i = 1; i <= hn; ++i) {
            scanf("%d%d", &h[i].p, &h[i].s);
        }
        sort(h + 1, h + 1 + hn);
        stop = 0;
        for(int i = 1; i <= hn; ++i) {
            while(stop && h[i].p >= h[st[stop]].p)
                --stop;
            st[++stop] = i;
        }

        hn = stop;
        for(int i = 1; i <= hn; ++i)
            H[i] = h[st[i]];

        for(int i = 1, curid = 1; i <= n; ++i) {
            while(curid <= hn && H[curid].s < i)
                ++curid;
            if(H[curid].s >= i)
                id[i] = curid;
            else
                id[i] = -1;
        }

        cur = 0;
        int ans = 0;
        while(cur < n) {
            int d = solve();
            if(d == -1) {
                ans = -1;
                break;
            } else {
                cur += d;
                ++ans;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

注:要筛选出一个有用的偏序集合的时候,貌似是排序然后单调栈是最好理解的。在这题中,因为维度s的取值范围足够小,可以不进行排序而使用dp来进行转移,具体做法就是在每个耐力值上打上最大的pi标记,然后从n向1传递pi,这样形成的就是一个偏序集合,复杂度O(n+m)。

原文地址:https://www.cnblogs.com/KisekiPurin2019/p/11854682.html

时间: 2024-08-30 08:28:16

Educational Codeforces Round 76 (Rated for Div. 2)的相关文章

Educational Codeforces Round 76 (Rated for Div. 2) E. The Contest

Educational Codeforces Round 76 (Rated for Div. 2) E. The Contest(dp+线段树) 题目链接 题意: 给定3个人互不相同的多个数字,可以把数字移动给别人,问最少移动几次后可以使第一个人的数字为1~m1,第二个人m1~m2,第三个人m2~n(可以没有数字) 题解: 设c[1][i]为第一个人m1为i时需要移动的次数,c[3][i]为m2为i是第三个人需要操作的次数,当其他两个人数字合法时,第二个人的数字也会合法.枚举第一个人的每个i,

Educational Codeforces Round 76 (Rated for Div. 2) - D. Yet Another Monster Killing Problem(贪心)

题意:有$n$个怪物,每个怪物有一个能力值$a[i]$,你现在有$m$个英雄,每个英雄有两个属性:$p[i]$表示这个英雄的能力值,$s[i]$表示这个英雄的耐力值,即一天内最多能消灭$s[i]$个怪物,每一天你可以选择一个英雄去消灭怪物,并且你只能一个一个的消灭,不能改变顺序,当一个英雄的能力值大于等于怪物的能力值并且他这一天内消灭的怪物数小于$s[i]$时,他就会继续去消灭下一个怪物,直到消灭的怪物数量等于$s[i]$或者这个英雄的能力值小于当前怪物的能力值,结束这一天,进入第二天,一个英雄

Educational Codeforces Round 69 (Rated for Div. 2) B - Pillars

Educational Codeforces Round 69 (Rated for Div. 2) B - Pillars There are n pillars aligned in a row and numbered from 1 to n. Initially each pillar contains exactly one disk. The i-th pillar contains a disk having radius ai. You can move these disks

Educational Codeforces Round 71 (Rated for Div. 2) D - Number Of Permutations

原文链接:https://www.cnblogs.com/xwl3109377858/p/11405773.html Educational Codeforces Round 71 (Rated for Div. 2) D - Number Of Permutations You are given a sequence of n pairs of integers: (a1,b1),(a2,b2),…,(an,bn). This sequence is called bad if it is

Educational Codeforces Round 36 (Rated for Div. 2)

Educational Codeforces Round 36 (Rated for Div. 2) F. Imbalance Value of a Tree You are given a tree T consisting of n vertices. A number is written on each vertex; the number written on vertex i is ai. Let's denote the function I(x,?y) as the differ

Educational Codeforces Round 36 (Rated for Div. 2) 题解

Educational Codeforces Round 36 (Rated for Div. 2) 题目的质量很不错(不看题解做不出来,笑 Codeforces 920C 题意 给定一个\(1\)到\(n\)组成的数组,只可以交换某些相邻的位置,问是否可以将数组调整为升序的 解题思路 首先如果每个数都能通过交换到它应该到的位置,那么就可以调整为升序的. 但实际上交换是对称的,如果应该在的位置在当前位置前方的数都交换完成,那么整体就是排好序的,因为不可能所有不在相应位置的数都在相应位置的后方.

Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes

Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes 题目连接: http://codeforces.com/contest/985/problem/E Description Mishka received a gift of multicolored pencils for his birthday! Unfortunately he lives in a monochrome w

Educational Codeforces Round 55 (Rated for Div. 2)

Educational Codeforces Round 55 (Rated for Div. 2) 链接 A Vasya and Book 傻逼题..注意判边界. #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<map> #include<vector> #include<cm

Educational Codeforces Round 57 (Rated for Div. 2)

get人生第二场CF! 成绩:(exACM) rank858 AC3/7 Penalty57 rating1648(+52) 题目:Educational Codeforces Round 57 (Rated for Div. 2) 错题题解: D. Easy Problem E. The Top Scorer F. Inversion Expectation G. Lucky Tickets 原文地址:https://www.cnblogs.com/xht37/p/10198321.html