下笔春蚕食叶声。

Codeforces Round #778 (Div. 1 + Div. 2, based on Technocup 2022 Final Round) A-E 题解

CF1654

打得一团糟,并且没开long long见祖宗,吃了FST。

A. Maximum Cake Tastiness

题意:至多进行一次区间翻转操作,使得 \(\max \{a_i+a_{i+1}\}\) 最大
题解:显然可以把任意的 \(a_i\)\(a_j\) 翻到一块去,选最大和次大就行了,懒得写 \(O(n)\),发现 \(O(n^2)\) 能过。

B. Prefix Removals

题意:一个字符串,每次选最长的在字符串中出现了两次及以上的前缀,把这个前缀删掉。输出最终剩下的字符串。
题解:如果当前字符串的第一个字符在后面出现过,肯定会被删掉。如果没出现过肯定删不掉。所以找到所有字符最后一次出现的位置的min就好了。(即:发现当前字符 \(s_i\)\([i,n]\) 中只出现了一次就可以break)

C. Alice and the Cake

题意:Alice 有一块大小为 \(sum\) 的蛋糕,她每次可以选择将她现有的所有块中的一块 \(w\) 大小的切成分别是 \(\lfloor \frac w 2 \rfloor\)\(\lceil \frac w 2 \rceil\) 大小的两块蛋糕。给出一个最终蛋糕序列,问Alice 可不可能切出这么个蛋糕。
题解:如果Alice没发现最终答案序列里有当前块大小的蛋糕,她肯定得切当前块。如果有,她肯定留着不切,因为切这块并留着后面一块和它一样大的,和留着这块并切后面一块和它一样大的,是本质相同的行为。但是无解的时候会不会T呢?答案是不会,因为最差情况就是它每次都扫到底然后发现答案序列里有大小为1的一块,那也顶多 \(n\log n\) 次 就会发现无解了。(应该是吧,实际上我是瞎嘴巴的。

map<ll, int>mp;//哈哈。没开long long,我一口一个fst。
bool solve(ll x) {
    if(mp[x]) { mp[x]--; return 1; }
    if(x == 1) return 0;
    if(solve(x / 2) && solve((x + 1) / 2)) return 1;
    return 0;
}

D. Potion Brewing Class

题意:给你 \(n\) 个数,\(n-1\) 个关系,表示 \(a_i:a_j=x:y\)。保证能够确定他们之间的比例,求所有 \(a_i\) 都是正整数时的最小 \(\sum a_i\)
题解:

  • 显然是一棵树,然后所有分母求 lcm,再带回去算。于是大家\(^{[1]}\)一起美妙卡D,我满脑子 \(n\) 个极其大的数求lcm,并将 \(lcm \bmod 998244353\) 输出。
  • Alaso_Shuang:D首先对1~n分解质因数,这个预处理,然后你维护一个bucket代表每个质因数当前的出现次数,每次新走一条边x/y 的时候,枚举每个x的指引数,bucket[p]--,枚举每个y的质因数,bucket[p]++,然后开一个maxn数组,开始maxn都是0,如果bucket[p]>maxn[p]了,val*=p,val初始为1,val就是点1的最小权值,然后dfs一遍就好了吧

  • 流泪了。其实maxn里面每个质因子最多出现 \(O(n\log n)\) 次。显然是可行的。说明我没有脑子。
关于具体实现
void update(int id, int va) {
    int xx = x[id], yy = y[id];
    int i;
    for(i = 2; i * i <= max(xx, yy); i++) {
        while(xx % i == 0) xx /= i, bk[i] -= va;
        while(yy % i == 0) yy /= i, bk[i] += va;
        mx[i] = max(mx[i], bk[i]);
    }
    i = xx; bk[i] -= va;
    if(xx != yy) mx[i] = max(mx[i], bk[i]);
    i = yy; bk[i] += va;
    mx[i] = max(mx[i], bk[i]);
    return;
}

EricQian: 这个合并我写的线段树合并,怎么写啊??

E. Arithmetic Operations

题意:把一个数组 \(a\) 篡改成一个等差数列(具体你定,只要是等差数列就好了)。问你最少修改次数。(修改一个数算一次)
题解:
首先你观察这个一组数据,\(n\le {10}^5\),没有多组数据,这很不CF,很像根号。
那么有什么可以用来根号分治呢,发现这个值域也是 1e5。
如果把公差定的很大或者很小,肯定是不优的。

  • \(abs(d) \le \sqrt n\):先扫一遍,可以选择出一个最优的 \(a_1\),然后扫一遍算答案。 \(O(n\sqrt n)\)

  • \(abs(d) > \sqrt n\)
    正常的数:指一个 \([1,10^5]\) 范围内的数。
    答案里肯定有正常的数,因为修改 \(n-1\) 个的方案肯定能构造出来。
    \(d > \sqrt n\),那么从一个正常的数出去 \(\sqrt n\) 个,就到了不正常的数去了,肯定要修改了。
    所以我们枚举那个正常的数 \(a_i\),然后我们需要考虑公差是多少。
    它左右出去 \(\sqrt n\) 个的数,都是可能不用修改的数。再出去就肯定要修改了,所以公差只和这 \(2\sqrt n\) 个数有关。
    那么我们考虑 \(a_i\)\(a_j\) 如果都不修改,那么公差会是多少。然后就可以知道最优公差。

    最优公差的绝对值并不 $> \sqrt n$,并不会寄 如果最优公差的绝对值并不 $> \sqrt n$,是否会造成错误,使得答案变小或变大? 不会使得答案变小:因为在这种情况中,较小的公差会把一些不用修改的数认为是“不正常”的数,比最终答案要大,实际上 $\le sqrt(n)$ 中的情况会覆盖它。 不会使得答案变大:而它已经是当前最优公差,没有比它更优的了,所以也不会使得答案变大。
  • 不要使用map,会被卡常。

  • \(d\le \sqrt n\) 的情况中,最后扫一遍统计答案的时候,忘记加公差,也能过所有样例。

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define pb push_back
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define ls(x) ((x) << 1)
#define rs(x) ((x) << 1 | 1)
#define fi first
#define se second
const int N = 1e5 + 10, M = 2e5 + 2 * 320 * N, K = 1e5 + 317 * N;
int n, B, mp[M];
ll a[N];
int calc(ll d) {
    ll val = a[1]; int ret = 0;
    for(int i = 1; i <= n; i++) {
        mp[K + a[i] - d * (i - 1)]++;
        if(mp[K + a[i] - d * (i - 1)] > mp[K + val])
            val = a[i] - d * (i - 1);
    }
    for(int i = 1; i <= n; i++) {
        if(a[i] == val) ret++;
        val += d;
    }
    for(int i = 1; i <= n; i++)
        mp[K + a[i] - d * (i - 1)]--;
    return ret;
}
int main(){
    scanf("%d", &n); B = 317;
    for(int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    int ans = 0;
    for(int i = -B; i <= B; i++)
        ans = max(ans, calc(i));
    for(int i = 1; i <= n; i++) {
        ll val = 0;
        for(int j = max(1, i - B); j < i; j++)
            if((a[i] - a[j]) % (i - j) == 0) {
                ll t = (a[i] - a[j]) / (i - j);
                mp[t + K]++; if(mp[t + K] > mp[val + K]) val = t;
            }
        for(int j = min(n, i + B); j > i; j--)
            if((a[j] - a[i]) % (j - i) == 0) {
                ll t = (a[j] - a[i]) / (j - i);
                mp[t + K]++; if(mp[t + K] > mp[val + K]) val = t;
            }
        ans = max(ans, mp[val + K] + 1);
        for(int j = max(1, i - B); j < i; j++)
            if((a[i] - a[j]) % (i - j) == 0)
                 mp[(a[i] - a[j]) / (i - j) + K]--;
        for(int j = min(n, i + B); j > i; j--)
            if((a[j] - a[i]) % (j - i) == 0) 
                mp[(a[j] - a[i]) / (j - i) + K]--;
    }
    printf("%d\n", n - ans);
    return 0;
}

虽然但是,这次ZCETHAN怎么没发题解啊

我明白了,ZCETHAN觉得太简单了,一下就AK了

[1]:”大家“ 指 Felix

posted @ 2022-03-21 14:32  ACwisher  阅读(223)  评论(2编辑  收藏  举报