Codeforces Round #538 (Div. 2) (CF1114)

Codeforces Round #538 (Div. 2) (CF1114)

??今天昨天晚上的cf打的非常惨(仅代表淮中最低水平

??先是一路缓慢地才A掉B,C,然后就开始杠D。于是写出了一个O(n^2)的线性dp,然后就wa6,调到结束。结束后发现完全看漏了两句话。噢,起始点!!!

??好吧然后算算自己有可能这一场要变成+0,反正在0左右。
结束后开始然后开始写D,顺便思考F。结果写完D发现A怎么fst了,然后。。。因为习惯于对相似的语句复制粘贴,有些东西没有改——三句话都在 -a!!!(这个还能过pt?

??好吧想完F以后又顺便看了一下。发现怎么B也fst了???发现好像忘了考虑相同的数的问题。。。

??还好C没有fst。所以大概不多不少,可以把上一场涨的分数给抵掉。

??也是wph学长说的好,这些都是用血换来的教训啊。(但是看错题真的不应该,这是在NOIP就犯过的错啊。


A. Got Any Grapes?

??这种题目直接做,显然是先尽量供给Andrew,然后是Dmitry,最后是Michal。

??希望大家不要犯我犯过的错误。(以后记得复制粘贴相似内容的时候注意修改全所有该修改的东西啊)

int x, y, z, a, b, c;

inline void End() {puts("NO"); exit(0);}

int main() {
    read(a), read(b), read(c);
    read(x), read(y), read(z);
    if (x < a) End(); else x -= a;
    y += x;
    if (y < b) End(); else y -= b;
    z += y;
    if (z < c) End(); else z -= c;
    puts("YES");
}

B. Yet Another Array Partitioning Task

??CF上的B题一般都是大胆猜结论题目。

??直接猜结论: 一定可以选齐前 \(m \cdot k\) 大的数。然后分的时候只要凑齐 \(m\) 个在前 \(m \cdot k\) 大的数中的数,就可以切一块。

??注意一下(也是我fst的原因),如果前 \(m \cdot k\) 中最小的数没有被选全的话,那么分的时候要注意判断一下那个数已经选了多少个,不够选了就不要把它算上去。

const int N = 2e5 + 7;
int n, m, k, p, a[N], b[N];
ll ans;
std::map<int, int> mp;

int main() {
    read(n), read(m), read(k); p = m * k; --k;
    for (int i = 1; i <= n; ++i) read(a[i]), b[i] = a[i];
    std::sort(b + 1, b + n + 1);
    for (int i = n - p + 1; i <= n; ++i) ans += b[i], mp[b[i]]++;
    printf("%I64d\n", ans);
    for (int i = 1, cnt = 0; i <= n; ++i) {
        if (mp.count(a[i]) && mp[a[i]]) ++cnt, --mp[a[i]];
        if (cnt == m) --k, printf("%d%c", i, " \n"[k == 0]), cnt = 0;
        if (!k) return 0;
    }
}

C. Trailing Loves (or L‘oeufs?)

??在 \(b\) 进制下末尾有 \(k\) 个0,那么说明

\[
\quad b ^ k | n!
\]

??于是我们把 \(b\) 分解质因数
\[
(p_1^{k_1} \cdot p_2^{k_2} \cdot \cdots) ^k | n!
\]
??于是我们发现
\[
k = \min\{\frac{n!}{p_1},\ \frac{n!}{p_2},\ \cdots\ \}
\]
??至于说 \(\frac{n!}{p}\) 怎么求,这个应该是普及组知识了。
\[
\frac{n!} p = \sum_{i = 1} \lfloor \frac n {p ^ i} \rfloor
\]

const int N = 1e6 + 7;
ll n, m, ans = 0x7fffffffffffffff;
int np[N], p[N], prt, cnt[N];

inline void Make_Prime(int n ){
    np[0] = np[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (!np[i]) p[++prt] = i;
        for (int j = 1; j <= prt && i * p[j] <= n; ++j) {
            np[i * p[j]] = 1;
            if (i % p[j]) break;
        }
    }
}

inline ll GetNum(ll n, ll x) {
    ll ans = 0;
    while (n) ans += n /= x;
    return ans;
}

int main() {
    read(n), read(m);
    Make_Prime(sqrt(m));
    ll hkk = m;
    for (int i = 1; i <= prt; ++i)
        while (hkk % p[i] == 0) hkk /= p[i], ++cnt[i];
    for (int i = 1; i <= prt; ++i) if (cnt[i]) SMIN(ans, GetNum(n, p[i]) / cnt[i]);
    if (hkk > 1) SMIN(ans, GetNum(n, hkk));
    printf("%I64d\n", ans);
}

D. Flood Fill

??这道题一开始没看见起始方块这个东西,一直wa6。

??如果有起始点,那就是区间dp模板了。

??设 \(dp[i][j]\) 表示 \(i..j\) 的这段区间全部化成一种颜色的代价。
\[
dp[i][j] = \left\{
\begin{align*}
&dp[i+1][j-1] &&c[i] = c[j]\&\min\{dp[i][j-1], dp[i][j+1]\} + 1 &&c[i] \neq c[j]
\end{align*}
\right.
\]

const int N = 5000 + 7;
const int INF = 0x3f3f3f3f;

int n, m, c[N], dp[N][N];

int main() {
    read(n);
    for (int i = 1; i <= n; ++i) read(c[i]), SMAX(m, c[i]);
    n = std::unique(c + 1, c + n +1) - c - 1;
    for (int i = n; i; --i)
        for (int j = i + 1; j <= n; ++j)
            if(c[i] == c[j]) dp[i][j] = dp[i + 1][j - 1] + 1;
            else dp[i][j] = std::min(dp[i][j - 1], dp[i + 1][j]) + 1;
    printf("%d\n", dp[1][n]);
}

E. Arithmetic Progression

??交互题娱乐身心。

??显然我们一个二分就可以很开心地求出最大值。

??然后我们就可发现,任意两个数的差都应该是公差的倍数。于是我们多随机一些位置(当然不能直接问前30个,防止毒瘤会卡),把位置上的值与最大值的差算出来,然后求这些差的 \(gcd\) 即可。

??不会证明正确率。

const int N = 1e6 + 7;
int n, L, R, stp, used[N];

int main() {
    read(n); srand(time(0));
    int l = 0, r = 1e9;
    while (l < r) {
        int mid = (l + r) >> 1, get;
        printf("> %d\n", mid); fflush(stdout);
        read(get);
        if(get) l = mid + 1;
        else r = mid;
    }
    R = l;
    for (int i = 1, get = 0; i <= 30 && i <= n; ++i) {
        int pos = rand() % n + 1;
        while(used[pos]) pos = rand() % n + 1;
        used[pos] = 1;
        printf("? %d\n", pos); fflush(stdout);
        read(get);
        stp = std::__gcd(stp, R - get);
    }
    printf("! %d %d\n", R - (n - 1) * stp, stp);
    fflush(stdout);
}

F. Please, another Queries on Array?

??回顾一下欧拉函数的公式。
\[
\varphi(n) = n \sum_{p\text{是}n\text{质因数}} 1-\frac1p
\]
??所以我们只需要线段树维护区间乘积,以及每个质数出没出现过。

??一开始打算用bitset,但是发现 \(300\) 以内质数只有 \(62\) 个,不多不少,可以直接 ull存。大概 ll也就够了。

??注意区间乘积,乘标记在区间上算贡献要以幂的形式算上去,而不是像求区间和那样直接乘。一开始没注意到,死活卡不过去。还有如果是用 ull压位的,注意算集合的时候 1 << i要写成1ull << i

#define lc o << 1
#define rc o << 1 | 1

const int N = 4e5 + 7;
const int M = 300 + 7;
const int P = 1e9 + 7;

int n, m, x, y, z, a[N];
char opt[15];

int prt, p[M], np[M], inv[N], id[N];
inline void Make_Prime(int n) {
    np[0] = np[1] = inv[1] = 1;
    for (int i = 2; i <= n; ++i) {
        inv[i] = (ll)(P - P / i) * inv[P % i] % P;
        if (!np[i]) p[++prt] = i, id[i] = prt;
        for (int j = 1; j <= prt && i * p[j] <= n; ++j){
            np[i * p[j]] = j;
            if (i % p[j] == 0) break;
        }
    }
}

inline pli operator + (const pli &a, const pli &b) {return pli(a.fi | b.fi, (ll)a.se * b.se % P);}
inline int fpow(int x, int y) {
    int ans = 1;
    for (; y; y >>= 1, x= (ll)x * x % P) if(y & 1) ans = (ll)ans * x % P;
    return ans;
}

struct Node {
    ull val, add;
    int mul, tag;
} t[N << 2];
inline void Build(int o, int L, int R) {
    t[o].tag = 1;
    if (L == R) {
        int x = a[L]; t[o].mul = a[L];
        while (x > 1 && np[x]) t[o].val |= 1ull << (np[x] - 1), x /= p[np[x]];
        if (x > 1) t[o].val |= 1ull << (id[x] - 1); return;
    }
    int M = (L + R) >> 1;
    Build(lc, L, M); Build(rc, M + 1, R);
    t[o].val = t[lc].val | t[rc].val; t[o].mul = (ll)t[lc].mul * t[rc].mul % P;
}
inline void Mul(int o, int L, int R, int l, int r, int x, ull y) {
    if (l <= L && R <= r) {
        t[o].tag = (ll)t[o].tag * x % P;
        t[o].mul = (ll)t[o].mul * fpow(x, R - L + 1) % P;
        t[o].add |= y; t[o].val |= t[o].add; return;
    }
    int M = (L + R) >> 1;
    if (l <= M) Mul(lc, L, M, l, r, x, y);
    if (r > M) Mul(rc, M + 1, R, l, r, x, y);
    t[o].val = t[lc].val | t[rc].val | t[o].add;
    t[o].mul = (ll)t[lc].mul * t[rc].mul % P *fpow(t[o].tag, R - L + 1) % P;
}
inline pli Get(int o, int L, int R, int l, int r, pli add = pli(0, 1)) {
    if (l <= L && R <= r) return pli(t[o].val, t[o].mul) + pli(add.fi, fpow(add.se, R - L + 1));
    int M = (L + R) >> 1; pli hkk = add + pli(t[o].add, t[o].tag);
    if (r <= M) return Get(lc, L, M, l, r, hkk);
    if (l > M) return Get(rc, M + 1, R, l, r, hkk);
    return Get(lc, L, M, l, r, hkk) + Get(rc, M + 1, R, l, r, hkk);
}

inline int GetAns(pli x) {
    int ans = x.se; ull S = x.fi;
    for (int i = 1; i <= prt; ++i)
        if((S >> (i - 1)) & 1) ans = (ll)ans * inv[p[i]] % P * (p[i] - 1) % P;
    return ans;
}

int main() {
    #ifdef hzhkk
    freopen("hkk.in", "r", stdin);
    #endif
    read(n), read(m); Make_Prime(300);
    for (int i = 1; i <= n; ++i) read(a[i]);
    Build(1, 1, n);
    for (int i = 1; i <= m; ++i) {
        scanf("%s", opt); read(x), read(y);
        if (*opt == 'M') {
            read(z); ull hkk = 0; int r = z;
            while (r > 1 && np[r]) hkk |= 1ull << (np[r] - 1), r /= p[np[r]];
            if (r > 1) hkk |= 1ull << (id[r] - 1);
            Mul(1, 1, n, x, y, z, hkk);
        }
        else printf("%d\n", GetAns(Get(1, 1, n, x, y)));
    }
}

原文地址:https://www.cnblogs.com/hankeke/p/CF1114.html

时间: 2024-10-08 12:51:38

Codeforces Round #538 (Div. 2) (CF1114)的相关文章

Codeforces Round #538 (Div. 2)

目录 Codeforces 1114 A.Got Any Grapes? B.Yet Another Array Partitioning Task C.Trailing Loves (or L'oeufs?) D.Flood Fill(区间DP) E.Arithmetic Progression(交互 二分 随机化) F.Please, another Queries on Array?(线段树 欧拉函数) Codeforces 1114 比赛链接 貌似最近平均难度最低的一场div2了...

Codeforces Round #538 (Div. 2) D. Flood Fill 【区间dp || LPS (最长回文序列)】

任意门:http://codeforces.com/contest/1114/problem/D D. Flood Fill time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output You are given a line of nn colored squares in a row, numbered from 11 to nn f

Codeforces Round #538 (Div. 2) E 随机数生成

https://codeforces.com/contest/1114/problem/E 题意 交互题,需要去猜一个乱序的等差数列的首项和公差,你能问两种问题 1. 数列中有没有数比x大 2. 数列的第i项是什么 最多只能问60次 题解 首先用第一种问题+二分问出数列最大的数是多少,最多二十次 然后用第二种问题尽可能分散的询问第i项,然后将问出的数组排序,对相邻两个数的差求gcd 随机数生成链接 https://codeforces.com/blog/entry/61587 https://c

[codeforces]Round #538 (Div. 2) F. Please, another Queries on Array?

题解:    $$  ans=F\left ( \prod _{i=l}^{r}a_i \right ) $$ $$ =(p_i-1){p_i}^{k_i-1}*.....*(p_j-1){p_j}^{k_j-1} $$ $$={p_i}^{k_i}*.....*{p_j}^{k_j}*(\frac{p_i-1}{p_i}*......*\frac{p_j-1}{p_j})  $$ 因为数据范围保证$ a_i\leq 300 $ 所以在这个范围内只有62个素因子  我们可以直接每一位对应一bit

[比赛] Codeforces Round #538 (Div. 2) solution (贪心,数学其他,二分,线段树)

已经写了100篇题解啦! link solution pdf #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read(){ int f=1,ans=0;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i

Codeforces Round #424 (Div. 2) D. Office Keys(dp)

题目链接:Codeforces Round #424 (Div. 2) D. Office Keys 题意: 在一条轴上有n个人,和m个钥匙,门在s位置. 现在每个人走单位距离需要单位时间. 每个钥匙只能被一个人拿. 求全部的人拿到钥匙并且走到门的最短时间. 题解: 显然没有交叉的情况,因为如果交叉的话可能不是最优解. 然后考虑dp[i][j]表示第i个人拿了第j把钥匙,然后 dp[i][j]=max(val(i,j),min(dp[i-1][i-1~j]))   val(i,j)表示第i个人拿

Codeforces Round #424 (Div. 2) C. Jury Marks(乱搞)

题目链接:Codeforces Round #424 (Div. 2) C. Jury Marks 题意: 给你一个有n个数序列,现在让你确定一个x,使得x通过挨着加这个序列的每一个数能出现所有给出的k个数. 问合法的x有多少个.题目保证这k个数完全不同. 题解: 显然,要将这n个数求一下前缀和,并且排一下序,这样,能出现的数就可以表示为x+a,x+b,x+c了. 这里 x+a,x+b,x+c是递增的.这里我把这个序列叫做A序列 然后对于给出的k个数,我们也排一下序,这里我把它叫做B序列,如果我

[Codeforces] Round #352 (Div. 2)

人生不止眼前的狗血,还有远方的狗带 A题B题一如既往的丝帛题 A题题意:询问按照12345678910111213...的顺序排列下去第n(n<=10^3)个数是多少 题解:打表,输出 1 #include<bits/stdc++.h> 2 using namespace std; 3 int dig[10],A[1005]; 4 int main(){ 5 int aa=0; 6 for(int i=1;;i++){ 7 int x=i,dd=0; 8 while(x)dig[++dd