CF Round 775 Div2 题解

A题 Game

给定一个长度为 \(n\) 的一维线性地图,位置 \(i\) 处可能是陆地或者水。

现在我们要从位置 \(1\) 到达位置 \(n\) 处(保证这两个地方都是水),我们有两种方式:

  1. 如果位置 \(i\) 和位置 \(i+1\) 都是陆地,那么可以不耗费代价来互相移动
  2. 可以从位置 \(x\) 跳到位置 \(y\),消耗代价为 \(|y-x|\) (该操作至多进行一次)

问需要花费至少多少代价来从位置 \(1\) 到达位置 \(n\)

\(1\leq n \leq 100\)

从头尾分别延伸最长的连续陆地串,然后中间直接跳即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n, a[N];
int solve() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    //
    bool flag = true;
    for (int i = 1; i <= n; ++i)
        if (a[i] == 0) flag = false;
    if (flag) return 0;

    int l, r;
    for (int i = 1; i <= n; ++i)
        if (a[i] == 0) {
            l = i - 1;
            break;
        }
    for (int i = n; i >= 1; i--)
        if (a[i] == 0) {
            r = i + 1;
            break;
        }
    return r - l;
}
int main()
{
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

B题 Game of Ball Passing (数学)

建议直接看原题面

我们对整个数组从小到大排个序,记整个数组前 \(n-1\) 个元素之和为 \(L\),最大元素的值为 \(R\)

\(L+1\geq R\) 的时候,不难构造出只含有一个球的方案。

\(L+1<R\) 时,我们发现,每次一个球都会使得 \(R\)\(L\) 的差值减小 1,所以答案为 \(R-L\)

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 100010;
int n;
LL a[N];
int solve() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    sort(a + 1, a + n + 1);
    if (a[n] == 0) return 0;
    LL L = 0, R = a[n];
    for (int i = 1; i < n; ++i) L += a[i];

    return max(R - L, 1LL);
}
int main()
{
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

C题 Weird Sum(前缀和)

给定一张 \(n\)\(m\) 列的地图,每个格子都有一个自己的颜色 \(c_{i,j}\)

我们记同一颜色 \(x\) 的格子的集合为 \(S_x\),那么 \(f(S_x)\) 为集合内格子两两曼哈顿距离之和,试求出 \(\sum\limits_{x\in M} f(S_x)\) 的值。(\(M\) 为地图中所有颜色之和)

\(1\leq n \leq m,nm\leq 10^6,1\leq c_{i,j}\leq 10^6\)

这个显然,我们开 \(10^6\) 个 vector,讲相同颜色的格子放入对应颜色的 vector 里面,然后直接对每个 vector 求解即可。

不过,我们平方枚举肯定不行,得优化到线性复杂度。

我们考虑坐标仅有一维的情况,那么我们直接将坐标排个序,然后做一次前缀和,按照算贡献的方式来统计答案,就能够将平方枚举优化到线性。

\[\sum\limits_{i=1}^{n-1}\sum\limits_{j=1+1}^n |x_j-x_i|=\sum\limits_{i=1}^{n-1}(\sum\limits_{j=i+1}^n x_j-(n-i)x_i) \]

对于二维的曼哈顿距离,由于两轴坐标互相独立,互不影响,所以我们直接分别排序统计后相加即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int C = 100010;
int n, m;
vector<pair<int, int> > color[C];
LL x[C], y[C], xx[C], yy[C];
LL solve(int c) {
    vector<pair<int, int> > &arr = color[c];
    int cnt = 0;
    for (auto it : arr) x[++cnt] = it.first, y[cnt] = it.second;
    sort(x + 1, x + cnt + 1);
    sort(y + 1, y + cnt + 1);
    for (int i = 1; i <= cnt; ++i)
        xx[i] = xx[i - 1] + x[i], yy[i] = yy[i - 1] + y[i];
    LL res = 0;
    for (int i = 1; i < cnt; ++i) {
        int len = cnt - i;
        res += (xx[cnt] - xx[i]) - x[i] * len;
        res += (yy[cnt] - yy[i]) - y[i] * len;
    }
    return res;
}
int main()
{
    //read
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        for (int j = 1, c; j <= m; ++j) {
            cin >> c;
            color[c].push_back(make_pair(i, j));
        }
    //solve
    LL ans = 0;
    for (int i = 1; i < C; ++i) ans += solve(i);
    cout << ans << endl;
    return 0;
}

D题 Integral Array(倍数枚举)

给定一个数组 \(\{a_n\}\),问是否对于内部任意两个元素 \(x,y(x\geq y)\)\(\lfloor\frac{x}{y}\rfloor\) 也在数组内?

\(1\leq n\leq 10^6,1\leq a_i\leq c \leq 10^6\)

一个偏暴力的 TLE 写法

我们将数组排个序并 unique 一下,然后从小到大依次将数放进去,每次加入一个新数字的时候,只枚举 \(\sqrt{a_i}\) 范围内的数,如果这个数存在,就看看整除的结果在不在数组内;如果不在,则看范围 \([L,R]\) 之内的数是否存在(\(L,R\) 满足 \(a_i\) 除以区间内的数得到的结果都等于这个数,可以 \(O(1)\) 的计算 \(L,R\))。考虑到单点修改和区间查询,所以我们维护一个 set 或者树状数组/线段树。

总复杂度 \(O(n\sqrt c\log c)\),对于 \(10^5\) 的数据还能挣扎一下,但是 \(10^6\) 属实有点为难了。

正解:倍数枚举

我们一开始就创建好 vis 数组并做一次前缀和(我们上面那个方法是动态插入的,所以有一个 \(O(\log n)\) 的复杂度,这里直接是一开始全部处理好,然后实现 \(O(1)\) 查询)。

我们枚举每一种值,倘若这个值存在,我们记为 \(x\),那么我们直接枚举 \(\lfloor\frac{y}{x}\rfloor\) 的值,记为 \(k\),那么有 \(kx\leq y <k(x+1)\)。我们看看区间 \([kx,kx+x-1]\) 内是否存在数,如果存在,那么我们就要查询一下 \(k\) 是否存在了。

这个的复杂度就是纯 \(O(n+c\sqrt{c})\),其实也就是少了一个 \(O(\log c)\) 的复杂度。

#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
int n, c, a[N];
//
int vis[N], pre[N];
inline bool query(int L, int R) { return pre[R] > pre[L - 1]; }
//
bool solve()
{
    //read
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    //init
    memset(vis, 0, sizeof(int) * (c + 1));
    for (int i = 1; i <= n; ++i)
        vis[a[i]] = 1;
    for (int i = 1; i <= c; ++i)
        pre[i] = pre[i - 1] + vis[i];
    //solve
    for (int x = 1; x <= c; ++x) {
        if (!vis[x]) continue;
        for (int k = 1; k * x <= c; ++k)
            if (query(k * x, min(c, k * x + x - 1)) && !vis[k])
                return false;
    }
    return true;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--) puts(solve() ? "Yes" : "No");
    return 0;
}
posted @ 2022-03-07 15:06  cyhforlight  阅读(47)  评论(3编辑  收藏  举报