Codeforces Round #636 (Div. 3)部分题解

链接:Codeforces Round #636 (Div. 3)
A - Candies
题意:求出一个x满足x+2∗x+4∗x+⋯+2k−1∗x=n且k>1
思路:提出x得x∗(1+2+4+⋯+2k−1)=n,从小到大枚举k,直到满足n∣(1+2+4+⋯+2k−1)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

int T, n;

int main()
{
    
    
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d", &T);
    while (T--) {
    
    
        scanf("%d", &n);
        int now = 3, t = 4;
        while (0 != n % now) {
    
    
            now += t;
            t *= 2;
        }
        printf("%d\n", n / now);
    }
    return 0;
}

B - Balanced Array
题意:给你一个偶数n,要求你构造出一个数列,满足前n2个数为偶数,后n2个数为奇数,每个数都为互不相同的正整数,且前n2个数的和等于后n2个数的和
思路:奇数和偶数的差为奇数,如果n2−1为奇数,那么对于前n2−1对奇数和偶数,合起来的差为偶数,那么第n2对奇数和偶数就无法构造出来,所以n2必须为偶数,即n∣4,显然可以构造出这样的数列2 4 6⋯n 1 3 5⋯(n−3) (n+n2−1)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

int T, n;

int main()
{
    
    
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d", &T);
    while (T--) {
    
    
        scanf("%d", &n);
        if (n % 4) {
    
    
            printf("NO\n");
            continue;
        }
        printf("YES\n");
        for (int i = 1; i <= n / 2; i++) printf("%d ", 2 * i);
        for (int i = 1; i <= n / 2 - 1; i++) printf("%d ", 2 * i - 1);
        printf("%d\n", n + n / 2 - 1);
    }
    return 0;
}

C - Alternating Subsequence
题意:就是给你n个数,你需要找出其中的最长子序列,并且这个子序列是正负交替的,并且尽量让这个子序列的和最大
思路:由于要子序列最长,所以把n个数按照正负分成几块(块内元素正负性相同),然后再在每个块内贪心取一个数即可

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long ll;

const int N = 200010;
const ll INF = 1000000000000000000;

int T, n;
ll a[N];

void solve(ll &imax, ll &imin, int l, int r)
{
    
    
    for (int i = l; i <= r; i++) {
    
    
        imax = max(imax, a[i]);
        imin = max(imin, a[i]);
    }
}

int main()
{
    
    
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d", &T);
    while (T--) {
    
    
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        int lst = 1, now = 2;
        ll res = 0;
        while (now <= n) {
    
    
            if ((a[now] > 0) != (a[now - 1] > 0)) {
    
    
                ll imax = -INF, imin = -INF;
                solve(imax, imin, lst, now - 1);
                if (a[lst] > 0) res += imax;
                else res += imin;
                lst = now;
            }
            now += 1;
        }
        ll imax = -INF, imin = -INF;
        solve(imax, imin, lst, n);
        if (a[lst] > 0) res += imax;
        else res += imin;
        printf("%lld\n", res);
    }
    return 0;
}

D - Constant Palindrome Sum
题意:给定一个长度为n的数列,n为偶数,保证每个元素在[1,k]之间,每次操作可以把某个位置的数字变成[1,k]内的任意数字,要求让这个数列满足:对于所有的i∈[1,n2],a[i]+a[n-i+1]是一个定值,问最少的操作次数
思路:设imin=min(a[i],a[n-i+1]),imax=max(a[i],a[n-i+1]),分三种情况,不进行操作a[i]+a[n-i+1]不变,操作一次能够使a[i]+a[n-i+1]∈[imin+1,imax+k],操作两次能使a[i]+a[n-i+1]∈[2,2k],根据贪心的思想,对于每一对(a[i],a[n-i+1]),[imin+1,imax+k]操作次数加1,[2,imin]和[imax+k+1,2k]操作次数加2,a[i]+a[n-i+1]操作次数不变,用差分数组维护一下,最后找到[2,2*k]内的最小操作次数即可

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 400010;
const int INF = 0x3f3f3f3f;

int T, n, k, a[N], b[N];

void add(int *d, int l, int r, int x)
{
    
    
    d[l] += x;
    d[r + 1] -= x;
}

int main()
{
    
    
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d", &T);
    while (T--) {
    
    
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= 2 * k; i++) b[i] = 0;
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for (int i = 1; i <= n / 2; i++) {
    
    
            int imin = min(a[i], a[n - i + 1]);
            int imax = max(a[i], a[n - i + 1]);
            if (2 <= imin) add(b, 2, imin, 2);
            add(b, imin + 1, imax + k, 1);
            if (imax + 1 <= k) add(b, imax + k + 1, 2 * k, 2);
        }
        for (int i = 1; i <= 2 * k; i++) b[i] += b[i - 1];
        for (int i = 1; i <= n / 2; i++) b[a[i] + a[n - i + 1]] -= 1;
        int res = INF;
        for (int i = 2; i <= 2 * k; i++) res = min(res, b[i]);
        printf("%d\n", res);
    }
    return 0;
}

E - Weights Distributing
题意:给定一张n个顶点的有权无向图,m条边和m个权值和三个点a,b,c。问如何分配边权能使a到b,b再到c的权值和最小,求最小值
思路:很直观的感受就是,求出a到b,b再到c的最短路径,然后将m个权值从小到大分配即可,如果同时有多条最短路径,我们应该选择重合最多的两条最短路径(因为这样用的边会更少),并且此时重合的部分应该分配权值最小的边,所以我们可以用bfs或者dfs求出a,b,c到其他所有点的最短距离da[],db[],dc[],然后枚举每个点x,假设a到b,b再到c的路径拆为a到x,x到b,b到x,x再到c,枚举所有的点x,一定会包含上述的两种(两条路径重叠与不重叠)的情况,并且此时b到x应该分配最小的边权,将m个边权排序,求前缀和sum[],那么对于点x,答案就是sum[db[x]]+sum[da[x]+db[x]+dc[x]]
参考

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>

using namespace std;

typedef long long ll;

const int N = 200010;
const int INF = 0x3f3f3f3f;

struct node {
    
    
    int to, nex;
};

int T, n, m, a, b, c;
ll p[N], sum[N];
int head[N], cnt, da[N], db[N], dc[N];
node edge[2 * N];
queue<int> q;

void init()
{
    
    
    cnt = 0;
    for (int i = 1; i <= n; i++) head[i] = 0;
    for (int i = 1; i <= n; i++) da[i] = db[i] = dc[i] = INF;
}

void add_edge(int u, int v)
{
    
    
    edge[++cnt].to = v;
    edge[cnt].nex = head[u];
    head[u] = cnt;
}

void bfs(int s, int *d)
{
    
    
    while (!q.empty()) q.pop();
    d[s] = 0;
    q.push(s);
    while (!q.empty()) {
    
    
        int u = q.front();
        q.pop();
        for (int i = head[u]; 0 != i; i = edge[i].nex) {
    
    
            int v = edge[i].to;
            if (d[v] != INF) continue;
            d[v] = d[u] + 1;
            q.push(v);
        }
    }
}

int main()
{
    
    
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d", &T);
    while (T--) {
    
    
        scanf("%d%d%d%d%d", &n, &m, &a, &b, &c);
        init();
        for (int i = 1; i <= m; i++) scanf("%lld", &p[i]);
        sort(p + 1, p + m + 1);
        for (int i = 1; i <= m; i++) sum[i] = sum[i - 1] + p[i];
        for (int i = 1; i <= m; i++) {
    
    
            int u, v;
            scanf("%d%d", &u, &v);
            add_edge(u, v);
            add_edge(v, u);
        }
        bfs(a, da);
        bfs(b, db);
        bfs(c, dc);
        ll res = 1000000000000000000;
        for (int i = 1; i <= n; i++) {
    
    
            if (da[i] + db[i] + dc[i] > m) continue;
            res = min(res, sum[db[i]] + sum[da[i] + db[i] + dc[i]]);
        }
        printf("%lld\n", res);
    }
    return 0;
}

编写人:刘枝飞

猜你喜欢

转载自blog.csdn.net/m0_50511504/article/details/108414726