Codeforces Round #600 (Div. 2)

A - Single Push

题意:给数组a和数组b,可以选择一段连续的区间[l,r]使得ai全部加k(k>0)至多一次。求能不能从a变成b。

题解:一开始作差排序去重,然后判断差是不是只有1个(且>=0)或只有两个(且c1=0,c2>0),但这样是错的,比如下面的样例。

1
5
1 1 1 1 1
2 1 2 2 2

因为虽然差都是1但不是连续的区间。

做法是作差,然后判断是否有至多一个正的方波。可以用两个变量,一个记录是否进入了方波,且方波的高是多少。另一个记录是否离开了方波。

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

const int mod = 1e9 + 7;

ll pow_mod(ll x, int n) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

int n, a[100005], b[100005];

void test_case() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &b[i]);
        b[i] -= a[i];
    }
    int in = 0, out = 0;
    for(int i = 1; i <= n; ++i) {
        if(b[i] < 0) {
            puts("NO");
            return;
        } else if(b[i] > 0) {
            if(in == 0)
                in = b[i];
            else if(out || in != b[i]) {
                puts("NO");
                return;
            }
        } else if(in == 1)
            out = 1;
    }
    puts("YES");
    return;
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

但第二天早上发现方波其实是什么?数列后面加上一个0,那么一个方波的差分就一定至多各有一个+a和-a。

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

const int mod = 1e9 + 7;

ll pow_mod(ll x, int n) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

int n, a[100005], b[100005];

void test_case() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &b[i]);
        b[i] -= a[i];
    }
    b[n + 1] = 0;
    for(int i = n + 1; i >= 1; --i)
        b[i] -= b[i - 1];

    int tmp = 0;
    for(int i = 1; i <= n + 1; ++i) {
        if(b[i] > 0) {
            if(tmp == 0)
                tmp = b[i];
            else {
                puts("NO");
                return;
            }
        } else if(b[i] < 0) {
            if(tmp == -b[i])
                tmp = -1;
            else {
                puts("NO");
                return;
            }
        }
    }
    if(tmp == 0 || tmp == -1)
        puts("YES");
    else
        puts("NO");
    return;
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

假如把n+1的项去掉,也可以“检测不到bi<0”则认为成功。

还有一种写法是,作差之后把两边的0缩掉,然后判断中间的区间都全等。

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

const int mod = 1e9 + 7;

ll pow_mod(ll x, int n) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

int n, a[100005], b[100005];

void test_case() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &b[i]);
        b[i] -= a[i];
    }
    int L = 1, R = n;
    while(L <= R && b[L] == 0)
        ++L;
    while(L <= R && b[R] == 0)
        --R;
    bool suc = 1;
    if(L <= R) {
        if(b[L] < 0)
            suc = 0;
        else {
            for(int i = L; i <= R; ++i) {
                if(b[i] != b[L]) {
                    suc = 0;
                    break;
                }
            }
        }
    }
    if(suc)
        puts("YES");
    else
        puts("NO");
    return;
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

B - Silly Mistake

题意:有个打卡机,序号a的人打卡上班+a,打卡下班-a。但是分隔天数的记录没了。规定每天每人只能上班至多一次,且上班后必须下班。求一种划分天数的方法,无解输出-1。贪心构造一种划分天数最多的方法,每次所有人下班之后就开一天新的,这样不容易导致同一个人上班多次(当然假如遇到这个上班过的人再开一天新的就是划分天数最少的方法)。记得最后一天要判断所有人都下班了!

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

const int mod = 1e9 + 7;

ll pow_mod(ll x, int n) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

int n, a[100005], cnt[1000005], vis[1000005];
int ans[50005], atop;

void test_case() {
    //memset(cnt, 0, sizeof(cnt));
    //memset(vis, 0, sizeof(vis));

    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);

    atop = 0;
    int pre = 0, cntm = 0;
    for(int i = 1; i <= n; ++i) {
        if(a[i] > 0) {
            ++cnt[a[i]];
            ++vis[a[i]];
            if(cnt[a[i]] == 1)
                ++cntm;
            else {
                puts("-1");
                return;
            }
        } else {
            --cnt[-a[i]];
            if(cnt[-a[i]] == 0) {
                --cntm;
                if(cntm == 0) {
                    for(int k = pre + 1; k <= i; ++k) {
                        if(a[k] < 0)
                            continue;
                        if(vis[a[k]] > 1) {
                            puts("-1");
                            return;
                        }
                        vis[a[k]] = 0;
                    }
                    ans[++atop] = i;
                    pre = i;
                }
            } else {
                puts("-1");
                return;
            }
        }
    }
    if(cntm != 0) {
        puts("-1");
        return;
    }

    for(int i = atop; i >= 1; --i)
        ans[i] -= ans[i - 1];
    printf("%d\n", atop);
    for(int i = 1; i <= atop; ++i)
        printf("%d%c", ans[i], " \n"[i == atop]);
    return;
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    //scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

当然去重还是用nlogn的方法最方便,比如来个set,insert之前看一下,这样写起来(或许)快,没有上面的这么复杂。

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

时间: 2024-08-30 13:22:36

Codeforces Round #600 (Div. 2)的相关文章

Codeforces Round #600 (Div. 2) E. Antenna Coverage

Codeforces Round #600 (Div. 2) E. Antenna Coverage(dp) 题目链接 题意: m只蚂蚁,每只蚂蚁的位置是\(x_i\),分数是\(s_i\),覆盖范围是\([x_i - s_i; x_i + s_i]\),每个硬币可以使一直蚂蚁的\(s_i\)+1,求覆盖整个\([1;m]\)的最少硬币 思路: \(f[pos][0]\)表示\([1,pos]\)没有被覆盖还要花费的最少硬币,\(f[pos][1]\)表示\([1,pos]\)被覆盖的最小花费硬

【cf比赛记录】Codeforces Round #600 (Div. 2)

Codeforces Round #600 (Div. 2) ---- 比赛传送门 昨晚成绩还好,AC A,B题,还能上分(到底有多菜) 补了C.D题,因为昨晚对C.D题已经有想法了,所以补起题来也快.(C题TLE了,D题想用并查集没好) A // http://codeforces.com/contest/1253/problem/A /* 如果YES,则b[i] - a[i] 在一个区间里的差肯定是相同的且不小于0 */ #include<iostream> #include<cst

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

Codeforces Round #600(Div.2) https://codeforces.com/contest/1253/problem A.Single Push 思路:数组各位相减,得到b-a之后的.如果全为0,或者只有一段非0且数字相同则可行,否则不可行.具体实现的话,可以左右两边指针向中间搜到第一个不为0的数,再判断中间是否均为同一个数.复杂度\(O(n)\). 注意:多组数据一定要判断是否需要清空.这里我a[n+1]没有清0,结果WA on test55-- AC代码: #in

第五天打卡 Codeforces Round #600 (Div. 2)

A题: #include<bits/stdc++.h> using namespace std; #define ll long long const int N = 1e5+10; int a[N],b[N]; int main(){ int t; scanf("%d",&t); while(t--) { int n; scanf("%d",&n); int need = -9999999; bool ok = true; for(in

Codeforces Round #535 (Div. 3) 题解

Codeforces Round #535 (Div. 3) 题目总链接:https://codeforces.com/contest/1108 太懒了啊~好久之前的我现在才更新,赶紧补上吧,不能漏掉了. A. Two distinct points 题意: 给出两个区间的左右边界,输出两个数,满足两个数分别在两个区间内且这两个数不相等. 题解: 直接输出左端点然后判断一下就行了. 代码如下: #include <bits/stdc++.h> using namespace std; type

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i

Codeforces Round #424 (Div. 2) D. Office Keys(dp)

题目链接:Codeforces Round #424 (Div. 2) D. Office Keys 题意: 在一条轴上有n个人,和m个钥匙,门在s位置. 现在每个人走单位距离需要单位时间. 每个钥匙只能被一个人拿. 求全部的人拿到钥匙并且走到门的最短时间. 题解: 显然没有交叉的情况,因为如果交叉的话可能不是最优解. 然后考虑dp[i][j]表示第i个人拿了第j把钥匙,然后 dp[i][j]=max(val(i,j),min(dp[i-1][i-1~j]))   val(i,j)表示第i个人拿

Codeforces Round #424 (Div. 2) C. Jury Marks(乱搞)

题目链接:Codeforces Round #424 (Div. 2) C. Jury Marks 题意: 给你一个有n个数序列,现在让你确定一个x,使得x通过挨着加这个序列的每一个数能出现所有给出的k个数. 问合法的x有多少个.题目保证这k个数完全不同. 题解: 显然,要将这n个数求一下前缀和,并且排一下序,这样,能出现的数就可以表示为x+a,x+b,x+c了. 这里 x+a,x+b,x+c是递增的.这里我把这个序列叫做A序列 然后对于给出的k个数,我们也排一下序,这里我把它叫做B序列,如果我

[Codeforces] Round #352 (Div. 2)

人生不止眼前的狗血,还有远方的狗带 A题B题一如既往的丝帛题 A题题意:询问按照12345678910111213...的顺序排列下去第n(n<=10^3)个数是多少 题解:打表,输出 1 #include<bits/stdc++.h> 2 using namespace std; 3 int dig[10],A[1005]; 4 int main(){ 5 int aa=0; 6 for(int i=1;;i++){ 7 int x=i,dd=0; 8 while(x)dig[++dd