1. 程式人生 > >Codeforces Round #538 (Div. 2) (CF1114)

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

andrew har 一個 any exit 結論題 end 發現 prime

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)));
    }
}

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