1. 程式人生 > 其它 >Codeforces Round #631 (Div. 1)

Codeforces Round #631 (Div. 1)

Codeforces Round #631 (Div. 1)

A

B

由題意得,\(a_i\) 二進位制下最高位的 \(1\) 一定大於 \(a_{i-1}\) 的,並且只需要滿足這一個條件。那麼只需要計算出 \(p_i\) 表示最高二進位制位為 \(i\) 時有多少種數,前面都是 \(2^k\) 的形式,最後一個要特判。然後每一位也可以不選,但不能全都不選,所以 \(res=(\prod(p_i+1))-1\)

void solve () {
    int d, m, res = 1;
    read (d), read (m);
    for (int i = 0; i <= 30; ++i) {
        if ((1ll << i + 1) - 1 < d) {
            res = res * ((1 << i) + 1) % m;
        } else {
            res = res * (d - (1 << i) + 2) % m; break;
        }
    }
    printf ("%lld\n", (res + m - 1) % m);
}

C

貪心的刪除,每次刪能刪的最大的那個(略)

D

\(a\)\(a_i=a_{i+1}\) 的地方被分割,弄一個新的字串 \(b\) 只記錄這些分割的字元,即依次在 \(b\) 中加入 \(a_i=a_{i+1}\)\(a_i\)。把在 \(a\) 中的刪除對應到 \(b\) 中就是:1、刪除一個 \(b_i\);2、刪除滿足 \(b_i\ne b_{i+1}\)\(i,i+1\)。也就是要儘量多用操作 \(2\) 刪光 \(b\)

設當前出現最多的是 \(x\),出現了 \(c\) 次。

1、\(2c \ge |b|\),可以讓其它所有的字元和 \(x\) 來一次操作 \(2\)

,用棧維護一下,剩下的 \(x\) 單獨刪。

2、\(2c< |b|\),先貪心的執行操作 \(2\),直到 \(2c=|b|\),然後變成第一種情況

輸出方案需要用線段樹維護一下當前的真實下標

const int N = 2e5 + 5;
int n, m, now, cnt[30], mx, p; char a[N];
int tp, t[N], res;
vector<pair<int, int> > ans;
pair<int, int> b[N];
#define fi first
#define se second
struct tree {
    int c[N << 2], tag[N << 2];
    #define ls (p << 1)
    #define rs (p << 1 | 1)
    void clear () {
        for (int i = 1; i <= 4 * n; ++i) c[i] = tag[i] = 0;
    }
    void push_down (int p, int l, int r) {
        if (!tag[p]) return;
        int mid (l + r >> 1);
        c[ls] = mid - l + 1, c[rs] = r - mid;
        tag[ls] = tag[rs] = 1; tag[p] = 0;
    }
    void modify (int p, int l, int r, int ql, int qr) {
        if (ql <= l && r <= qr) {
            c[p] = r - l + 1, tag[p] = 1; return;
        }
        int mid (l + r >> 1); push_down (p, l, r);
        if (ql <= mid) modify (ls, l, mid, ql, qr);
        if (qr > mid) modify (rs, mid + 1, r, ql, qr);
        c[p] = c[ls] + c[rs];
    }
    int query (int p, int l, int r, int ql, int qr) {
        if (ql <= l && r <= qr) return c[p];
        int mid (l + r >> 1), s = 0; push_down (p, l, r);
        if (ql <= mid) s += query (ls, l, mid, ql, qr);
        if (qr > mid) s += query (rs, mid + 1, r, ql, qr);
        return s;
    }
} u;
void del (int l, int r) {
    int L = l - u.query (1, 1, n, 1, l);
    int R = r - u.query (1, 1, n, 1, r);
    if (!L) L = 1; if (!R) R = 1;
    ++res; ans.emplace_back (L, R);
    u.modify (1, 1, n, l, r);
    --cnt[a[l] - 'a'];
    if (r < n) --cnt[a[r] - 'a'];
}
void solve_a () {
    tp = 0;
    for (int i = 1; i <= m; ++i) {
        if (!tp) { t[++tp] = i; continue; }
        if (b[t[tp]].fi == p) {
            if (b[i].fi == p) t[++tp] = i;
            else del (b[t[tp]].se + 1, b[i].se), --tp;
        } else {
            if (b[i].fi != p) t[++tp] = i;
            else del (b[t[tp]].se + 1, b[i].se), --tp;
        }
    }
    if (!tp) return del (1, n), void ();
    while (tp > 1) del (b[t[tp - 1]].se + 1, b[t[tp]].se), --tp;
    del (1, b[t[1]].se), del (b[t[1]].se + 1, n);
}
void solve_b () {
    if (m & 1) del (b[m].se + 1, n), --m, --now;
    tp = 0;
    for (int i = 1; i <= m; ++i) {
        for (int j = 0; j < 26; ++j)
            if (cnt[j] * 2 >= now) {
                p = j; int mm = 0;
                for (int k = 1; k <= tp; ++k) b[++mm] = b[t[k]];
                for (int k = i; k <= m; ++k) b[++mm] = b[k];
                m = mm;
                return solve_a (), void ();
            }
        if (!tp || b[t[tp]].fi == b[i].fi)
            { t[++tp] = i; continue; }
        del (b[t[tp]].se + 1, b[i].se), --tp, now -= 2;
    }
    del (1, n);
}
signed main() {
    int T; read (T);
    while (T--) {
        scanf ("%s", a + 1);
        n = strlen (a + 1); m = 0;
        for (int i = 1; i < n; ++i)
            if (a[i] == a[i + 1]) b[++m] = {a[i] - 'a', i};
        memset (cnt, 0, sizeof (cnt));
        for (int i = 1; i <= m; ++i) ++cnt[b[i].fi];
        mx = 0; now = m;
        for (int i = 0; i < 26; ++i)
            if (cnt[i] > mx) mx = cnt[i], p = i;
        res = 0, ans.clear (), u.clear ();
        mx * 2 >= m ? solve_a () : solve_b ();
        printf ("%d\n", res);
        for (auto i : ans) printf ("%d %d\n", i.fi, i.se);
    }
    return 0;
}

E

原本存在的 \(A\) 將字串劃分為若干段,設每段長度為 \(l_i\),共 \(s\) 段。新的 \(A\) 一定是儘量平均分佈最優,那麼一組答案 \((L,R)\) 可行,需要滿足兩個條件

1、\([\lceil\frac{l_i}{R} \rceil-1,\lfloor\frac{l_i}{L}\rfloor-1]\) 區間非空

2、\(\sum\lceil\frac{l_i}{R} \rceil-s\leq k\leq \sum\lfloor\frac{l_i}{L}\rfloor-s\)

對於第二個條件,\(L,R\) 相互獨立,可以直接二分出來最大的 \(L'\) 和最小的 \(R'\)。然後分類

1、\(L'\ge R'\),答案為 \(0\)。此時取任意 \(L=R\in[R',L']\),第二個條件已經滿足,而對於第一個條件,因為 \(\sum\lceil\frac{l_i}{R} \rceil\leq \sum\lfloor\frac{l_i}{L}\rfloor\)\(\lceil\frac{l_i}{R} \rceil\ge \lfloor\frac{l_i}{L}\rfloor\),所以 \(\forall i,\lceil\frac{l_i}{R} \rceil=\lfloor\frac{l_i}{L}\rfloor\),第一個條件也成立。其實,若 \(L'\ge R'\),必有 \(L'=R'\)

2、\(L'<R'\),此時需要考慮第一個條件。\(\frac{l_i}{L'}>\frac{l_i}{R'}\Rightarrow \lfloor\frac{l_i}{L}\rfloor-\lceil\frac{l_i}{R} \rceil\ge-1\)。因為如果不滿足,差為 \(-1\),那麼最優方案只需要移動 \(L',R'\) 中的一個即可。也就是會多出一些限制 \((a_i,b_i)\),表示 \(L'\leq a_i\)\(R'\ge b_i\),選一些移動 \(L'\),一些移動 \(R'\),讓 \(R'-L'\) 最小。排個序算算

const int N = 4e5 + 5;
int n, m, k, p[N], l[N];
int L, R;
int f (int x, int o) {
    int s = 0;
    for (int i = 1; i <= m; ++i) s += l[i] / x;
    if (o) {
        for (int i = 1; i <= m; ++i) s += (l[i] % x != 0);
    }
    return s;
}
void getLR () {
    int l = 1, r = n, mid; L = 1;
    while (l <= r) {
        mid = l + r >> 1;
        int t = f (mid, 0);
        if (t < m + k) r = mid - 1;
        else L = mid, l = mid + 1;
    }
    l = 1, r = n, R = n;
    while (l <= r) {
        mid = l + r >> 1;
        int t = f (mid, 1);
        if (t > m + k) l = mid + 1;
        else R = mid, r = mid - 1;
    }
}
pair<int, int> a[N]; int mx[N];
#define fi first
#define se second
int solve () {
    int tot = 0, res = 1e16;
    for (int i = 1; i <= m; ++i) {
        int x = l[i] / R + (l[i] % R != 0), y = l[i] / L;
        if (x > y) {
            a[++tot].fi = l[i] / x;
            a[tot].se = y ? l[i] / y + (l[i] % y != 0) : 1e16;
        }
    }
    a[++tot] = {L, 1e16}, a[++tot] = {-1e16, R};
    sort (a + 1, a + tot + 1);
    for (int i = 1; i <= tot; ++i)
        mx[i] = max (mx[i - 1], a[i].se);
    for (int i = 2; i <= tot; ++i)
        res = min (res, mx[i - 1] - a[i].fi);
    return res;
}
signed main() {
    int T; read (T);
    while (T--) {
        read (n), read (m), read (k);
        ++m, p[1] = 0, p[m + 1] = n;
        for (int i = 2; i <= m; ++i) read (p[i]);
        for (int i = 1; i <= m; ++i) l[i] = p[i + 1] - p[i];
        getLR ();
        if (L >= R) { puts ("0"); continue; }
        printf ("%lld\n", solve ());
    }
    return 0;
}