Codeforces Round #666 (Div. 1)

A. Multiples of Length (CF 1396 A)

题目大意

给定一个\(n\)个数字的数组\(a\),你需要进行三次操作。

每次操作,选择一个区间,然后对区间里的每一个数,加上一个该区间长度的倍数。不同数加的数可以不同。

输出三次操作的区间以及该区间每个数所加的数。

可以证明保证有解。

解题思路

构造题。为应对任意数字的情况,我们让每个\(a_{i}\)的系数变为\(0\)

如果\(n = 1\),那么答案就是

\(1 \space \space 1\)

\(-a_{1}\)

\(1 \space \space 1\)

\(0\)

\(1 \space \space 1\)

\(0\)

否则就

\(1 \space \space 1\)

\(-a_{1}\)

\(1 \space \space n\)

\(0 \space \space -n \times a_{2} \space \space -n \times a_{3} \space \space \dots \space \space -n \times a_{n}\)

\(2 \space \space n\)

\((n-1) \times a_{2} \space \space (n-1) \times a_{3} \space \space \dots \space \space (n-1) \times a_{n}\)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int k;
    cin >> k;
    vector<LL> qwq;
    int n = k;
    while(k--){
        LL a;
        cin>>a;
        qwq.push_back(a);
    }
    if (n == 1){
        cout<<"1 1"<<endl;
        cout<<-qwq[0]<<endl;
        cout<<"1 1"<<endl;
        cout<<'0'<<endl;
        cout<<"1 1"<<endl;
        cout<<'0'<<endl;
        return 0;
    }
    cout<<"1 1"<<endl;
    cout<<-qwq[0]<<endl;
    cout<<"1 "<<n<<endl;
    qwq[0] = 0;
    for(size_t i = 0; i < qwq.size(); ++ i)
        cout<<-(LL)n*qwq[i]<<(i == qwq.size() - 1?'\n':' ');
    cout<<"2 "<<n<<endl;
    for(size_t i = 1; i < qwq.size(); ++ i)
        cout<<(LL)(n-1)*qwq[i]<<(i == qwq.size() - 1?'\n':' ');
    return 0;
}


B. Stoned Game (CF 1396 B)

题目大意

初始\(n\)堆石子,\(T\)\(HL\)轮流拿,\(T\)先。一个人不能拿上一轮对方拿的那堆石子。当两者均采取最优策略的情况下,谁必赢。

解题思路

博弈题。

很显然,\(n=1\)\(T\)赢。

\(n=2\)时,若两堆石子数相同,则\(HL\)赢,否则\(T\)赢。

其余的,他们采取最优策略,即总是拿石子数最多的那堆,模拟即可得到答案。

或者可以这样考虑,因为最后是剩下两堆石子的,而如果两堆石头数量相同,则\(HL\)赢。那么,我们假设最后剩下的是第\(i\)堆和第\(j\)堆,初始\(a_{i} < a_{j}\)\(HL\)为了赢,需要花\(a_{j} - a_{i}\)轮,使得这两堆相同,剩下的取其他堆,只要此时剩下的其他堆的数量是偶数,那么最后的局面就进入\(HL\)必赢局面。

也即\(\sum\limits_{k = 1}^{n} a_{k} - a_{i} - a_{j} - (a_{j} - a_{i}) = \sum\limits_{k = 1}^{n} a_{k} - 2a_{j}\)是偶数,那么\(HL\)必赢。

由于\(2a_{j}\)时偶数,不改变结果的奇偶性,所以判断\(\sum\limits_{k = 1}^{n} a_{k}\)的奇偶性即可。

特别的,如果最大堆的数量大于其他堆数量的和,则\(T\)必赢。

综上,如果最大堆的数量大于其他堆数量的和或\(\sum\limits_{k = 1}^{n} a_{k}\)是奇数,则\(T\)必赢,否则\(HL\)必赢。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int main(void) {
    int kase; read(kase);
    for (int ii = 1; ii <= kase; ii++) {
        int n;
        read(n);
        int tot = 0;
        vector<int> qwq(n);
        for(int i = 0; i < n; ++ i){
            read(qwq[i]);
            tot += qwq[i];
        }
        sort(qwq.begin(),qwq.end());
        if (n == 1) puts("T");
        else if (n == 2){
            if (qwq[0] == qwq[1]) puts("HL");
            else puts("T");
        }
        else {
            tot -= qwq[qwq.size() - 1];
            if (qwq[qwq.size() - 1] > tot) puts("T");
            else{
                tot -= qwq[qwq.size() - 1];
                if (tot & 1) puts("T");
                else puts("HL");
            }
        }
    }
    return 0;
}


C. Monster Invaders (CF 1396 C)

题目大意

\(n\)个关卡,初始第一个关卡,第\(i\)个关卡有\(a_{i}\)\(1HP\)的普通怪物和\(1\)\(2HP\)\(Boss\)怪物。

你有三种枪:

  • 手枪,对一只怪物造成\(1HP\)伤害,花费\(r_1\)时间
  • 激光枪,对所有怪物造成\(1HP\)伤害,花费\(r_2\)时间
  • AWP,直接干掉一只怪物,花费\(r_3\)时间

除了第二种,其余枪要在干掉该关卡的普通小怪后才能对\(Boss\)造成伤害。且如果对\(Boss\)造成伤害,且\(Boss\)未死时,必须转移到上一个或下一个关卡。当然,干掉完当前关卡的怪物需要手动转移到下一个关卡。

转移关卡花费\(d\)时间。

问干掉\(Boss\)需要的最短时间。

数据保证\(r_{1} \leq r_{2} \leq r_{3}\)

解题思路

干掉一个关卡的所有怪物,我们有三种方法。

  • 手枪+AWP
  • 激光枪+手枪
  • 手枪+手枪

第一种很简单,干完该关卡boss后就跳到下一个关卡。

关键就是第二个和第三个,它会跳到其他关卡,然后再跳回来。问题就是会往哪跳,跳多远。

首先任何往后跳的都可以看成是后来往前跳,所以方向就确定了。

然后可以猜想证明的是,最优情况下,一定是只往前跳一次。也就是来回两个关卡跳。

如果是来回三个关卡跳,需要的总时间有两个,一个是跳跃总时间,一个是干掉该层怪物所需要的时间。

后者时间是固定的,我们来看前者。

三个关卡来回跳的转移次数是6次,而三个关卡来回跳可以看成两个两个关卡的来回跳,同样是转移6次,两者时间相同。(从三个关卡最左边开始)

如果是来回四个关卡跳,则转移次数是9次,而两个两个关卡来回跳外加一次跳,转移次数是7次,后者更优。

所以我们就只用关心来回两个关卡来回跳即可。

\(f[i]\)表示干掉前\(i\)个关卡的怪物所需要的最小时间。

首先可以直接从第\(i - 1\)个关卡转移过来,然后采用三种方法中最小的一个干掉第\(i\)个关卡的怪物。

或者从第\(i - 2\)个关卡转移过来,考虑在第\(i\)个关卡和第\(i - 1\)个关卡来回跳,分别考虑第\(i\)个关卡采用三种方式和第\(i-1\)个关卡采用的三种方式,取最小值。

注意的是如果在考虑第\(n\)个关卡时,如果采用第一种方式,然后跳到第\(n-1\)关卡时,不需要再跳回到第\(i\)个关卡。

还有就是在考虑第\(1\)个关卡和第\(2\)个关卡来回跳的时候,不要算上\(0\)个关卡跳到第\(1\)个关卡的转移时间。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

const int N = 1e6 + 8;

LL r1, r2, r3;

LL a[N];

LL f[N];

LL d, n;

LL fuck0(int i){
    return r1 * a[i] + r3;
}

LL fuck1(int i){
    return r2 + r1;
}

LL fuck2(int i){
    return r1 * a[i] + r1 + r1;
}

int main(void) {
    read(n);
    read(r1);
    read(r2);
    read(r3);
    read(d);
    for(int i = 1; i <= n; ++ i)
        read(a[i]);
    f[1] = fuck0(1);
    for(int i = 2; i <= n; ++ i){
        f[i] = min({
                    f[i - 1] + d + fuck0(i),
                    f[i - 1] + d + d + d + min(fuck1(i), fuck2(i)),
                    f[i - 2] + d + d + d + d * (i != n) + min(fuck1(i - 1), fuck2(i - 1)) + fuck0(i),
                    f[i - 2] + (i != 2) * d + d + d + d + min(fuck1(i - 1), fuck2(i - 1)) + min(fuck1(i), fuck2(i))
                });
    }
    write(f[n], '\n');

    return 0;
}


posted @ 2020-09-03 13:16  ~Lanly~  阅读(313)  评论(1编辑  收藏  举报