2.24训练赛

A.P1127 词链

以字母为节点,单词为边建图,求无向图字典序最小的欧拉路径。

#include <bits/stdc++.h>
#define fu(a, b, c) for (ll a = b; a <= c; a++)
#define fd(a, b, c) for (ll a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
#define all(x) x.begin(), x.end()
#define sz(x) ll(x.size())
#define f first
#define s second
using namespace std;
using LL = __int128;
using ll = long long;
using ld = long double;
using pll = pair<ll, ll>;
using pii = pair<int, int>;
using psi = pair<string, int>;
using pdd = pair<double, double>;
constexpr int N = 'z' + 1;
int n, m, u, v, s, in[N], out[N];
vector<psi> g[N];
stack<string> st;

inline bool ck() {
    int c = 0;
    fu(i, 'a', 'z') {
        if (in[i] ^ out[i]) {
            ++c;
            if (out[i] == in[i] + 1)
                s = i;
        }
    }
    return !c || c == 2;
}

inline void dfs(int u) {
    while (sz(g[u])) {
        auto [s, v] = g[u].back();
        g[u].pop_back(), dfs(v);
        st.emplace(s);
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n;
    fu(i, 1, n) {
        string s;
        cin >> s, g[s[0]].emplace_back(s, s.back());
        ++out[s[0]], ++in[s.back()];
    }

    if (!ck())
        return cout << "***", 0;
    fu(i, 'a', 'z') {
        sort(all(g[i]), greater<psi>());
        if (!s && sz(g[i]))
            s = i;
    }

    dfs(s);
    if (sz(st) ^ n)
        return cout << "***", 0;
    while (sz(st))
        cout << st.top() << " ."[sz(st) > 1], st.pop();
}

B.P1129 矩阵游戏

显然是个二分图。

import io
import os
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
t = int(input())
for i in range(t):
    n = int(input())
    m = [[0] * n for i in range(n)]
    for i in range(n):
        m[i] = list(map(int, input().split()))
    f, r = [-1] * n, 0

    def dfs(x):
        for i in range(n):
            if m[x][i] and not u[i]:
                u[i] = 1
                if not ~f[i] or dfs(f[i]):
                    f[i] = x
                    return 1
        return 0
    for i in range(n):
        u = [0] * n
        r += dfs(i)
    print('No' if r ^ n else 'Yes')

C.P1128 求正整数

剪枝搜索+高精。

from math import sqrt
P = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
ans = 0


def dfs(n, m=1, p=0):
    global ans
    if ans and m >= ans:
        return
    if n == 1:
        ans = min(ans, m) if ans else m
        return
    r = int(sqrt(n))
    f = 0
    for i in range(2, r + 1):
        if not n % i:
            j = n // i
            dfs(j, m * P[p] ** (i - 1), p + 1)
            dfs(i, m * P[p] ** (j - 1), p + 1)
            f = 1
    if not p or not f:
        dfs(1, m * P[p] ** (n - 1), p + 1)


n = int(input())
dfs(n)
print(ans)

D.P4043 支线剧情

有向图,每条边最小覆盖一次,可以用最小费用可行流解。

#include <bits/extc++.h>
#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
#define sz(a) int(a.size())
#define f first
#define s second
using namespace __gnu_pbds;
using namespace std;
using pii = pair<int, int>;
constexpr int N = 310, M = 1.2e4;
int n, m, t, cnt = 1, C;
int he[N], h[N], d[N], dif[N];
pii p[N];

struct edge {
    int v, f, c, ne;
} e[M];

inline void add(int u, int v, int f, int c) {
    e[++cnt] = {v, f, c, he[u]}, he[u] = cnt;
    e[++cnt] = {u, 0, -c, he[v]}, he[v] = cnt;
}

bool dij() {
    __gnu_pbds::priority_queue<pii, greater<pii>> q;
    memset(d + 1, 60, t * sizeof d[0]);
    decltype(q.push({0, 0})) pt[t + 1]; // 用于修改的迭代器
    q.push({0, 0});

    while (sz(q)) {
        auto [val, u] = q.top();
        q.pop();
        for (int i = he[u]; i; i = e[i].ne) {
            int v = e[i].v, nc = val + e[i].c + h[u] - h[v];
            if (e[i].f && nc < d[v]) {
                d[v] = nc, p[v].f = u, p[v].s = i;
                if (pt[v] == 0)
                    pt[v] = q.push({d[v], v});
                else
                    q.modify(pt[v], {d[v], v});
            }
        }
    }
    return d[t] < 1e9;
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n, t = n + 1;
    int v, w;
    fu(i, 1, n) {
        cin >> m, dif[i] -= m; // 入边容量下界与出边容量下界之差
        fu(j, 1, m) {
            cin >> v >> w, ++dif[v];
            add(i, v, M, w), C += w;
        }
        if (i ^ 1)
            add(i, 1, M, 0);
    }
    fu(i, 1, n) if (dif[i] > 0) add(0, i, dif[i], 0);
    else if (dif[i] < 0) add(i, t, -dif[i], 0);

    while (dij()) {
        m = 1e9;
        fu(i, 1, t) h[i] += d[i];
        for (int i = t; i; i = p[i].f)
            mn(m, e[p[i].s].f);
        for (int i = t; i; i = p[i].f)
            e[p[i].s].f -= m, e[p[i].s ^ 1].f += m;
        C += m * h[t];
    }
    cout << C;
}

E.P5824 十二重计数法

\(n\)个球和\(m\)个盒子,要全部装进盒子里。

I:球之间互不相同,盒子之间互不相同,每个球都有\(m\)个选择即\(m^n\)

II:球之间互不相同,盒子之间互不相同,每个盒子至多装一个球,第\(i\)个球有\(m-i+1\)个选择,
\(\mathrm A(m, n) = m^{\underline n}\)(下降幂)。

III:球之间互不相同,盒子之间互不相同,每个盒子至少装一个球,容斥枚举至少有多少个空盒子
\(\sum_{i=0}^{m-1} (-1)^i {m \choose i} (m-i)^n\),如果盒子相同(VI),
那么这个答案要\(/m!\),即第二类斯特林数\(S(n, m)\)

IV:球之间互不相同,盒子全部相同,\(\sum_{i=1}^m S(n, i)\)

V:球之间互不相同,盒子全部相同,每个盒子至多装一个球,\([n<=m]\)

VI:球之间互不相同,盒子全部相同,每个盒子至少装一个球,\(S(n, m)\)

VII:球全部相同,盒子之间互不相同,插板法\(n+m-1 \choose m-1\)

VIII:球全部相同,盒子之间互不相同,每个盒子至多装一个球,\(m \choose n\)

IX:球全部相同,盒子之间互不相同,每个盒子至少装一个球,先给每个盒子装一个球,
然后插板法\(n-1 \choose m-1\)

X:球全部相同,盒子全部相同,生成函数,同
P4389 付公主的背包

XI:球全部相同,盒子全部相同,每个盒子至多装一个球,\([n<=m]\)

XII:球全部相同,盒子全部相同,每个盒子至少装一个球,先给每个盒子装一个球,然后同X。

#include <bits/extc++.h>
#include <bits/stdc++.h>
#define fu(a, b, c) for (ll a = b; a <= c; a++)
#define fd(a, b, c) for (ll a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
#define sz(a) ll(a.size())
#define f first
#define s second
using namespace __gnu_pbds;
using namespace std;
using ll = long long;
using poly = vector<ll>;
constexpr ll N = 4e5 + 1, N1 = (1 << 19) + 1, X = 3, M = 998244353;
ll n, m, mx, ans[13];
ll fac[N]{1};
ll r[20][N1], xi[N1]{1}, iv[N1]{0, 1}, ifac[N1]{1};
poly f, g, F;

ostream &operator<<(ostream &cout, vector<ll> &v) {
    for (int i : v)
        cout << (i + M) % M << ' ';
    cout << '\n';
    return cout;
}

inline ll up(ll n) { return 1 << __lg(n) + 1; }

inline ll C(ll m, ll n) {
    return n > m ? 0 : fac[m] * ifac[m - n] % M * ifac[n] % M;
}

inline ll A(ll m, ll n) {
    return n > m ? 0 : fac[m] * ifac[m - n] % M;
}

inline ll S(ll m) { return m > n ? 0 : f[m]; }

inline ll qpow(ll a, ll b) {
    ll r = 1;
    for (; b; b >>= 1, a = a * a % M)
        if (b & 1)
            r = r * a % M;
    return r;
}

inline void init_rev(ll l, ll n) {
    fu(i, 0, n - 1) r[l][i] = r[l][i / 2] / 2 | (i & 1 ? n / 2 : 0);
}

inline void ntt(poly &a, ll n, bool f) {
    a.resize(n);
    ll l = __lg(n);
    fu(i, 0, n - 1) if (i < r[l][i]) swap(a[i], a[r[l][i]]);
    ll p = mx >> 1;
    for (ll l = 1; l < n; l <<= 1, p >>= 1) {
        for (ll i = 0; i < n; i += l << 1) {
            for (ll j = i, x = 0; j < i + l; ++j, x += p) {
                ll b = xi[x] * a[j + l] % M;
                a[j + l] = (a[j] - b) % M, a[j] = (a[j] + b) % M;
            }
        }
    }
    if (!f) {
        reverse(&a[1], &a[n]);
        fu(i, 0, n - 1) a[i] = a[i] * iv[n] % M;
    }
}

inline poly der(const poly &a) {
    poly b(sz(a));
    fu(i, 0, sz(b) - 2) b[i] = ll(i + 1) * a[i + 1] % M;
    return b;
}

inline poly inte(const poly &a) {
    poly b(sz(a));
    fd(i, sz(b) - 2, 0) b[i + 1] = a[i] * iv[i + 1] % M;
    return b;
}

inline void mul(
    poly &a, poly b, ll n,
    function<ll(ll, ll)> f = [](ll a, ll b) { return a * b % M; }) {
    ntt(a, n, 1), ntt(b, n, 1);
    fu(i, 0, n - 1) a[i] = f(a[i], b[i]);
    ntt(a, n, 0);
}

inline poly inv(const poly &a) {
    poly b(1, qpow(a[0], M - 2));
    for (ll m = 2; m >> 1 < sz(a); m <<= 1) {
        poly c(&a[0], &a[m]);
        mul(b, c, m * 2, [](ll a, ll b) { return (2 - a * b) % M * a % M; });
        b.resize(m);
    }
    return b;
}

inline poly ln(const poly &a) {
    ll n = up(sz(a) * 2 - 1);
    poly b = der(a), c = inv(a);
    mul(b, c, n);
    return inte(b);
}

inline poly exp(const poly &a) {
    poly b(1, 1);
    for (ll m = 2; m >> 1 < sz(a); m <<= 1) {
        poly c = ln(b);
        c[0] = 1, c.resize(m);
        fu(i, 1, m - 1) c[i] = a[i] - c[i];
        mul(b, c, m * 2);
    }
    return b;
}

inline poly pow(const poly &a, ll k) {
    poly b = ln(a);
    b.resize(sz(a));
    fu(i, 0, sz(b) - 1) b[i] = b[i] * k % M;
    return exp(b);
}

inline void init() {
    mx = up(n + n + 1);
    fu(i, 2, mx) iv[i] = -M / i * iv[M % i] % M;
    xi[1] = qpow(X, (M - 1) / mx);
    fu(i, 2, mx) xi[i] = xi[i - 1] * xi[1] % M;
    fu(i, 1, mx) ifac[i] = iv[i] * ifac[i - 1] % M;
    fu(i, 1, n + m) fac[i] = i * fac[i - 1] % M;
    for (ll i = 1, l = 2; l <= mx; l <<= 1, ++i)
        init_rev(i, l);

    f.resize(n + 1), g.resize(n + 1); // 第二类斯特林数·行
    fu(i, 1, n) f[i] = qpow(i, n - 1) * ifac[i - 1] % M;
    fu(i, 0, n) g[i] = ifac[i] * (i & 1 ? -1 : 1) % M;
    mul(f, g, mx);

    F.resize(n + 1); // 对生成函数卷积求ln再exp
    fu(i, 1, m) fu(j, 1, n / i) F[i * j] += iv[j];
    fu(i, 1, n) F[i] %= M;
    F = exp(F);
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> m, init();

    ans[1] = qpow(m, n), ans[2] = A(m, n);
    ans[3] = S(m) * fac[m] % M;
    fu(i, 1, m) ans[4] += S(i);
    ans[4] %= M, ans[5] = n <= m, ans[6] = S(m);
    ans[7] = C(n + m - 1, m - 1), ans[8] = C(m, n);
    ans[9] = C(n - 1, m - 1), ans[10] = F[n];
    ans[11] = n <= m, ans[12] = n < m ? 0 : F[n - m];

    fu(i, 1, 12) cout << (ans[i] + M) % M << '\n';
}

F.CF1644C Increase Subarray Sums

容易想到一个贪心的做法(预处理所有线段+后缀最大值),这里用dp,\(dp[i][j]\)
表示\(k=i\)时用上\(a_j\)的最大值。

#include <bits/extc++.h>
#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
#define sz(x) int(x.size())
#define f first
#define s second
using namespace std;
using namespace __gnu_pbds;
constexpr int N = 1e6 + 1, M = 1e9 + 7;
int t, n, m, k, a, b, c, d, e, f;
int x[N];

signed main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> t;
    fu(_, 1, t) {
        cin >> n >> k;
        int y[n + 1][n + 1]{};
        fu(i, 1, n) cin >> x[i];
        fu(i, 0, n) {
            fu(j, 1, n) {
                mx(y[i][j], max(x[j], x[j] + y[i][j - 1]));
                i ? mx(y[i][j], y[i - 1][j - 1] + k + x[j]) : 0;
            }
        }
        fu(i, 0, n) cout << *max_element(y[i] + 1, y[i] + n + 1) << ' ';
        cout << '\n';
    }
}

G.P8146 [JRKSJ R4] risrqnis

\(m=1\)时用树状数组\(nlogn\),剩下的情况令\(n, q, m\)同阶,考虑根号分治,对操作数大于根号的集合
直接在分块序列上修改合法点。小于等于的用set维护新增值域段,离线每个值域段对查询的贡献(线性空间),
需要O(1)查询,根号修改。另外这题没给\(l, r, k\)范围,我测了一下有\(l \leq r\)\(op = 1\)\(l, r\)\(int\)
范围内,否则在\(1 \sim n\)内。\(1 \leq k \leq m\)

#include <bits/extc++.h>
#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
#define all(x) x.begin(), x.end()
#define sz(x) int(x.size())
#define f first
#define s second
using namespace std;
using namespace __gnu_pbds;
using ll = long long;
using pii = pair<int, int>;
using vit = vector<int>::iterator;
constexpr int N = 1e6 + 1, M = 3e5 + 1, B = 547, BN = (M + B - 2) / B + 1;
int n, q, m, k, a[N], fa[N], o[N], l[N], r[N], ans[N];
int bl[BN], br[BN], S[BN], bel[M], s[M], bn;
pii b[N];
unordered_map<int, vector<int>> op;
vector<pair<int, pair<vit, vit>>> g[M];

inline void in(int &x) {
    x = 0;
    char c = getchar();
    while (c < 48)
        c = getchar();
    while (c >= 48)
        x = x * 10 + c - 48, c = getchar();
}

inline void out(int x) {
    if (x >= 10)
        out(x / 10);
    putchar(x % 10 + 48);
}

int find(int x) { return x <= n && fa[x] ? fa[x] = find(fa[x]) : x; }

void add(int p) { ++s[p], ++S[bel[p]]; }; // O(1)修改

int qry(int p) { // 根号查询
    int no = bel[p], r = 0;
    fu(i, 1, no - 1) r += S[i];
    fu(i, bl[no], p) r += s[i];
    return r;
};

void clear() { memset(s + 1, 0, n << 2), memset(S + 1, 0, bn << 2); };

void cal(auto &add, auto &qry, auto &v) {
    for (int no : v) {
        if (o[no] == 1)
            for (int i = find(l[no]); i <= r[no]; i = find(i + 1))
                add(b[i].s), fa[i] = fa[i + 1] ? fa[i + 1] : i + 1;
        else
            ans[no] = qry(r[no]) - qry(l[no] - 1);
    }
};

void sol0() {
    static int T[N];

    auto add = [](int p) {
        while (p <= n)
            ++T[p], p += p & -p;
    };

    auto qry = [](int p) {
        int r = 0;
        while (p)
            r += T[p], p -= p & -p;
        return r;
    };

    cal(add, qry, op[1]);
};

void sol1(auto &v) {
    memset(fa + 1, 0, n << 2), clear();
    cal(add, qry, v);
};

void sol2(auto b, auto e) {
    set<pii> s{{0, 0}, {n + 1, 0}};        // 边界
    auto ins = [&](int l, int r, auto i) { // 把值域l, r插入s
        auto il = s.lower_bound({l + 1, 0}), ir = il--;
        for (; ir != s.end() && il->s < r; ++il, ++ir) {
            int L = max(l, il->s + 1), R = min(r, ir->f - 1);
            if (L <= R) { // 新增区间
                g[L - 1].emplace_back(-1, pair(i, e));
                g[R].emplace_back(1, pair(i, e));
            }
        }
        auto it = s.lower_bound({l + 1, 0}); // 合并
        while (it != s.end() && it->f <= r + 1)
            mx(r, it->s), it = s.erase(it);
        while (it != s.begin() && (--it)->s >= l - 1)
            mn(l, it->f), mx(r, it->s), it = s.erase(it);
        s.emplace(l, r);
    };
    for (; b != e; ++b) // 离线修改
        if (o[*b] == 1)
            ins(l[*b], r[*b], next(b));
};

void sol() {
    auto add = [](int p) { // 根号修改
        int no = bel[p];
        fu(i, p, br[no])++ s[i];
        fu(i, no + 1, bn)++ S[i];
    };

    auto qry = [](int p) { return s[p] + S[bel[p]]; }; // O(1)查询

    clear();
    fu(i, 1, n) {
        add(b[i].s);
        for (auto [w, p] : g[i])
            for (auto [b, e] = p; b != e; ++b)
                if (o[*b] == 2)
                    ans[*b] += w * (qry(r[*b]) - qry(l[*b] - 1));
    }
};

int main() {
    in(n), in(q), in(m);
    fu(i, 1, n) in(a[i]), b[i] = {a[i], i};
    sort(b + 1, b + n + 1);
    fu(i, 1, q) {
        in(o[i]), in(l[i]), in(r[i]), in(k);
        if (o[i] == 1) {
            assert(INT_MIN <= l[i] && l[i] <= r[i] && r[i] <= INT_MAX);
            l[i] = lower_bound(b + 1, b + n + 1, pii{l[i], 0}) - b;
            r[i] = lower_bound(b + 1, b + n + 1, pii{r[i] + 1, 0}) - b - 1;
            if (l[i] > r[i])
                continue;
        } else {
            assert(1 <= l[i] && l[i] <= r[i] && r[i] <= n);
        }
        op[k].emplace_back(i);
    }

    if (m == 1)
        sol0();
    else {
        bn = (n + B - 1) / B;
        fu(i, 1, bn) { // 分块
            bl[i] = br[i - 1] + 1, br[i] = min(n, br[i - 1] + B);
            fu(j, bl[i], br[i]) bel[j] = i;
        }

        for (auto &[no, v] : op)
            sz(v) > B ? sol1(v) : sol2(v.begin(), v.end());
        sol();
    }
    fu(i, 1, q) if (o[i] == 2) out(ans[i]), putchar('\n');
}

H.CF896C Willem, Chtholly and Seniorious

考虑用三元组\([l, r, v]\)表示一个区间,随机数据下的区间赋值操作会使区间数量减小,
因此暴力维护即可,关于复杂度的证明可以参考珂朵莉树的复杂度分析

#include <bits/extc++.h>
#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
#define all(x) x.begin(), x.end()
#define sz(x) int(x.size())
#define f first
#define s second
using namespace std;
using namespace __gnu_pbds;
using ll = long long;
using pli = pair<ll, int>;
constexpr int N = 1e5 + 10, M = 1e9 + 7;
int t, n, m, s, v;
struct node {
    int l, r;
    mutable ll v;
    node(int l, int r, ll v) : l(l), r(r), v(v) {}
    bool operator<(node x) const { return l < x.l; }
};
set<node> odt;

auto split(int x) {
    if (x > n)
        return odt.end();
    auto it = --odt.upper_bound({x, 0, 0});
    if (it->l == x)
        return it;
    int l = it->l, r = it->r;
    ll v = it->v;
    odt.erase(it), odt.emplace(l, x - 1, v);
    return odt.emplace(x, r, v).f;
}

void assign(int l, int r, int x) {
    auto ir = split(r + 1), il = split(l);
    odt.erase(il, ir), odt.emplace(l, r, x);
}

void add(int l, int r, int x) {
    auto ir = split(r + 1), il = split(l);
    for (; il != ir; ++il)
        il->v += x;
}

ll xth(int l, int r, int x) {
    auto ir = split(r + 1), il = split(l);
    vector<pli> v;
    for (; il != ir; ++il)
        v.emplace_back(il->v, il->r - il->l + 1);
    sort(all(v));
    int c = 0;
    for (auto [f, s] : v) {
        c += s;
        if (c >= x)
            return f;
    }
}

int qpow(int a, int b, const int p) {
    int r = 1;
    for (; b; b >>= 1, a = (ll)a * a % p)
        if (b & 1)
            r = (ll)r * a % p;
    return r;
}

int psum(int l, int r, int x, const int y) {
    ll rt = 0;
    auto ir = split(r + 1), il = split(l);
    for (; il != ir; ++il)
        rt += qpow(il->v % y, x, y) * ll(il->r - il->l + 1);
    return rt % y;
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> m >> s >> v;
    auto rnd = [&] {int r = s; s = (s * 7ll + 13) % M; return r; };
    fu(i, 1, n) odt.emplace(i, i, rnd() % v + 1);
    fu(i, 1, m) {
        int op = rnd() % 4 + 1;
        int l = rnd() % n + 1;
        int r = rnd() % n + 1;
        if (l > r)
            swap(l, r);
        int x = rnd() % (op ^ 3 ? v : r - l + 1) + 1, y;
        switch (op) {
        case 1:
            add(l, r, x);
            break;
        case 2:
            assign(l, r, x);
            break;
        case 3:
            cout << xth(l, r, x) << '\n';
            break;
        default:
            y = rnd() % v + 1;
            cout << psum(l, r, x, y) << '\n';
        }
    }
}
posted @ 2022-03-07 11:21  lemu  阅读(99)  评论(0编辑  收藏  举报
4