CF Round 765 Div2 题解

A题 Ancient Civilization

\(T(1\leq T \leq 100)\) 组数据。

定义 \(\operatorname{d}(x,y)\) 为数 \(x,y\) 间的距离,值为二进制下不同的位置的数量之和。(例如 \((10010)_2\)\((01011)_2\),这两个数的距离为 3)。

现在,我们有 \(n\) 个数,第 \(i\) 个数记为 \(x_i\)。现在尝试求出一个数 \(y\),使得 \(\sum\limits_{i=1}^n\operatorname{d}(x_i,y)\) 最小,并输出 \(y\)

\(n\leq 100,0\leq x_i,y < 2^l\),其中 \(l\) 为题目中给定的一个常数,且 \(1\leq l \leq 30\)

计算每一位的贡献,显然为了距离最小,优先让 \(y\) 的这一位和大多数保持一致(例如有 a 个数的第 xx 位为 1, b 个数的第 xx 位为 0,那么 \(y\) 的这一位应当根据 a 和 b 的大小关系来决定,哪个大选哪个)。对每一位均进行该操作,总复杂度为 \(O(Tln)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n, l, x[N];
int solve() {
    cin >> n >> l;
    for (int i = 1; i <= n; ++i)
        cin >> x[i];

    int res = 0;
    for (int i = 0; i < 30; ++i) {
        int a = 0, b = 0;
        for (int k = 1; k <= n; ++k)
            if ((x[k] >> i) & 1) a++;
            else b++;
        if (a > b) res += 1 << i;
    }
    return res;
}

int main()
{
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

B题 Elementary Particles

\(T(1\leq T \leq 100)\) 组数据。

给定一个长度为 \(n\) 的数列 \(\{a_n\}\)。如果存在两个子区间 \([l_1,r_1],[l_2,r_2]\),他们互不相同(即不可能 \(l_1=l_2\)\(r_1=r_2\))长度相同且存在某一位相同(例如 \([4,3,2,1]\)\([5,3,4,8]\),这两个区间就是长度相等,且第二位相同),那么我们称这两个区间满足性质A

问,我们在这个数列中能找到的最长的两个满足性质A的子区间的长度为多少?(无解时输出 -1

\(2\leq n \leq 150000,1\leq a_i\leq 150000,\sum n\leq 3*10^5\)

似乎只要某一位相同就能凑出一对区间,所以直接扫一遍,无相同元素则判无解。

如果有相同元素,那么即可以此进行拓展,来看看最多可以延申多长。我们假设位置 \(i,j\)\(i<j\))的元素相同,那么区间长度最长为 \((n-j)+(i-1)+1=n-(j-i)\)。(也就是说,这个元素作为整个区间的第 \(i\) 个元素,此时区间长度最长,推导过程就是依次让 \(a_j\) 为区间的第 \(1,2,\cdots,i\) 项)

有了数学基础,那么问题就转变为了:找出两个相同元素,且他们在数列上的距离最短。受多组数据和智育所限,我们不妨开一个 \(map\) 来维护每个元素的上一个位置,每当扫到一个新元素的时候都更新一下,并重新计算最短距离。总复杂度为 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 300010;
int n, a[N];
map<int, int> vis;
int solve() {
    vis.clear();

    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    int dis = n + 1;
    for (int i = 1; i <= n; ++i) {
        if (vis[a[i]]) dis = min(dis, i - vis[a[i]]);
        vis[a[i]] = i;
    }
    return n - dis;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--) printf("%d\n", solve());
    return 0;
}

C题 Road Optimization

题意略。

一个 DP,第一维记录当前要到第 \(i\) 个杆,第二位记录当前已经保留了 \(j\) 个杆(而不是拔掉了几个)。

这样写之后,状态转移方程就相对好写一些了:

\[dp(i,x)=\max_{1\leq j < i} dp(j,x-1)+a_j(d_i-d_j) \]

(注:要到第 \(i\) 个杆,说明还没有到,所以这个杆子暂时不考虑拔不拔,留在以后转移再处理

别的怎么统计答案,输出啥的就不细说了,看代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 510;
int n, k;
LL len, d[N], a[N];
LL dp[N][N];
int main()
{
    //read
    cin >> n >> len >> k;
    for (int i = 1; i <= n; i++)
        cin >> d[i];
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    //solve
    d[n + 1] = len;
    memset(dp, 0x3f, sizeof(dp));
    dp[1][0] = 0;

    for (int i = 1; i <= n + 1; i++)
        for (int j = 1; j < i; j++)
            for (int x = 1; x <= j; x++)
                dp[i][x] = min(dp[i][x], dp[j][x - 1] + a[j] * (d[i] - d[j]));

    LL ans = 1e18;
    for (int i = 0; i <= k; i++)
        ans = min(ans, dp[n + 1][n - i]);
    cout << ans;
    return 0;
}
posted @ 2022-01-17 22:29  cyhforlight  阅读(24)  评论(0编辑  收藏  举报