【题录】CF#666 Div.2

B.Power Sequence

易知\(c^{i}\)为递增序列,猜想当\(a_{i}\) 也为递增序列时总代价最小。

证明:假设有 \(i < j, a_{i}  \leq a_{j}\);

由 \(|x| + |y| = max{|x + y|, |x - y|}\) :

\(|a_{i} - c^{i}| + |a_{j} - c ^{j}| = max{|a_{i} + a_{j} - c_{i} - c_{j}|, |a_{i} - a_{j} - c^{i} + c^{j}| }\);

\(|a_{j} - c^{i}| + |a_{i} - c ^{j}| = max{|a_{i} + a_{j} - c_{i} - c_{j}|, |a_{j} - a_{i} - c^{i} + c^{j}| }\);

由于\(a_{j} - a_{i} \geq 0, c^{j} - c^{i} > 0\),所以 \(|a_{i} - c^{i}| + |a_{j} - c ^{j}| \leq |a_{j} - c^{i}| + |a_{i} - c ^{j}|\)

又由于c为指数级增长,所以暴力处理即可(n 小的时候 c 的可能性多,n 大的时候 c 的可能性小,依次枚举)

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 200000
    #define MAXC 1000000
    #define int unsigned long long
    #define INF 9123372036854775807LL
    int n, a[maxn], ans = INF;
     
    int read() {
        int x = 0, k = 1; char c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
        while(c >= '0' && c <= '9') { x = x * 10 + c - '0', c = getchar(); }
        return x * k;
    }
     
    int abs(int a, int b) {
        if(a < b) return b - a;
        else return a - b;
    }
     
    bool check(int c) {
        int tans = 0; bool ret = 1;
        for(int i = 0, t = 1; i < n; i ++, t *= c) {
            tans = tans + abs(a[i], t);
            if(a[i] < t && t - a[i] >= ans) {
                ret = 0;
                break;
            }
        }
        ans = min(ans, tans);
        return ret;
    }
     
    signed main() {
        n = read();
        for(int i = 0; i < n; i ++) a[i] = read();
        sort(a, a + n);
        for(int c = 1; c < MAXC; c ++)
            if(!check(c)) break;
        printf("%lld\n", ans);
        return 0;
    }

 

 

C.Multiples of Length

试想:若对一个数可以进行两次操作,第一次加上 \(p * a\),第二次加上 \(q * b\), 其中\(p, q\) 为变量, \(a, b\) 为不相等的常数,则该数一定可以变为 \(0\)。

由此,我们可以得出思路:

1.若只有一个数字:修改一次后每次变化为\(0\)。

2.若有一个以上的数字:可以第一步把第一个数字变为 \(0\)。第二步对剩下的 \(n - 1\) 个数字进行操作。第三步再对全体 \(n\) 个数字进行操作(第一个数字为 \(0\)不变,等价于 \(a = n - 1, b = n\))。联立方程对每一个 \(a_{i}\) 解出 \(p, q\) 输出。

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 400000
    #define int long long
    int n, a[maxn], rec[maxn];
     
    int read() {
        int x = 0, k = 1; char c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
        while(c >= '0' && c <= '9') { x = x * 10 + c - '0', c = getchar(); }
        return x * k;
    }
     
    signed main() {
        n = read();
        for(int i = 1; i <= n; i ++) a[i] = read();
        printf("1 1\n%lld\n", -a[1]);
        if(n == 1) {
            printf("1 1\n0\n1 1\n0\n");
            return 0;
        }
        printf("2 %lld\n", n);
        for(int i = 2; i <= n; i ++) {
            int s = a[i] % n, t = (a[i] - s) / n;
            printf("%lld", s * (n - 1));
            if(i != n) printf(" "); else printf("\n");
            rec[i] = t + s;
        }
        printf("1 %lld\n", n);
        printf("0 ");
        for(int i = 2; i <= n; i ++) {
            printf("%lld", -rec[i] * n);
            if(i != n) printf(" "); else printf("\n"); 
        }
        return 0;
    }

 

D.Stoned Game

记石头的总数为 \(S\)。A.若存在一堆石头数目超过 \(\frac{S}{2}\) ,则先手获胜(只需要一直拿这一堆即可)。

B.若并非如此,则所有石头数目 \(\leq \frac{S}{2}\)。

若石头的总数为偶数:若先手走完后为情况 A,则接下来的先手获得胜利,即后手胜利。若先手走完后仍为情况B,则此时后手可以随意选择一堆石头拿走一个(由于B的前提条件此时必然还有可以拿的石堆)。分析在这两步之前:\(a_{i} \leq \frac{S}{2}\),走完之后 \(S' = S - 2, \frac{S'}{2} = \frac{S}{2} - 1\)。由于第一步走完之后进入B的条件与此相同,可知再拿走第二个石子满足:\(a_{i} \leq \frac{S'}{2}\)。是以后手总有路可走,直到先手走完后为情况A胜利为止。

若石头的总数为奇数:先手可以随便拿走一个,原先:\(a_{i} \leq \frac{S - 1}{2}\), 现\(S' = S - 1\),总数变为偶数且仍为B情况。所以此时后手即先手胜利。

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 100000
    int a[maxn];
     
    int read() {
        int x = 0, k = 1; char c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
        while(c >= '0' && c <= '9') { x = x * 10 + c - '0', c = getchar(); }
        return x * k;
    }
     
    int main() {    
        int T = read();
        while(T --) {
            int n = read(), S = 0; bool mark = 0;
            for(int i = 1; i <= n; i ++) a[i] = read(), S += a[i];
            for(int i = 1; i <= n; i ++) 
                if(a[i] > (S >> 1)) { mark = 1; break; }
            if(mark || (!mark && (S & 1))) printf("T\n");
            else printf("HL\n");
        }
        return 0;
    }

 

posted @ 2020-09-03 20:27  Twilight_Sx  阅读(270)  评论(1编辑  收藏  举报