重新把蓝书刷一遍(不定期更新)

算法竞赛进阶指南

基本算法

位运算

a^b

ll a, b, p;

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

int main() {
    cin >> a >> b >> p;
    cout << qpow(a, b);
    return 0;
}

a*b

ll a, b, p;

ll qmul(ll a, ll b) {
    ll ans = 0;
    for (; b; b >>= 1, a = a * 2 % p) if (b & 1) ans = (ans + a) % p;
    return ans;
}

int main() {
    cin >> a >> b >> p;
    cout << qmul(a, b);
    return 0;
}

⭐最短Hamilton路径

状压已经走过的点, d[k][i] 表示状态以i结尾的最小花费

要求 1 到 n 的最短Hamiton路径, 起始 d[1][0] = 0, 要求 d[(1 << n) - 1][n - 1]

转移的时候切记 0 不能作为转移之后的结束点, 0 只能做 d[1][0] 的结束点

d[7][0] 由于 d[6][1 or 2] 本身达不到, 故不存在 d[7][0] = d[6][1 or 2] + mp[1 or 2][0],

所以我们不用考虑中间态的结束点为 0, 只要注意转义的状态不以 0 结束即可

int mp[20][20], n, d[1 << 21][20];

int main() {
    cin >> n; memset(d, 0x3f, sizeof d); d[1][0] = 0;
    for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) cin >> mp[i][j];
    for (int k = 1; k < (1 << n); ++k)
        for (int i = 0; i < n; ++i) if (k != i && (k >> i & 1))
            for (int j = 0; j < n; ++j) if (k >> j & 1 && j && j != i)
                d[k][j] = min(d[k][j], d[k ^ (1 << j)][i] + mp[i][j]);
    cout << d[(1 << n) - 1][n - 1];
    return 0;
}

起床困难综合征

int n, m, _, a = (1 << 30) - 1, b = 0;
char s[5];

int main() {
    cin >> n >> m; int ;
    while (cin >> s >> _)
        switch (s[0]) {
        case 'X' : a ^= _, b ^= _; break;
        case 'O' : a |= _, b |= _; break;
        case 'A' : a &= _, b &= _; break; 
        }
    int res = 0, ans = 0;
    for (int i = 29; ~i; --i) if (res ^ (1 << i) <= m)
        if (b >> i & 1) ans ^= 1 << i;
        else if (a >> i & 1) res ^= 1 << i, ans ^= 1 << i;
    cout << ans; 
    return 0;
}

递推与递归

递归实现指数型枚举

int n, a[15];

void work(int x, int k) {
    if (k > n) return; work(x, k + 1); a[x - 1] = k;
    for (int i = 0; i < x; ++i) cout << a[i] << ' '; cout << '\n';
    work(x + 1, k + 1);
}

int main() {
    cin >> n; cout << '\n'; work(1, 1);
    return 0;
}

递归实现组合型枚举

int n, m, a[25];

void work(int x, int k) {
    if (x > m) { for (int i = 0; i < m; ++i) cout << a[i] << ' '; cout << '\n'; return; }
    if (k > n) return;
    a[x - 1] = k; work(x + 1, k + 1); work(x, k + 1);
}

int main() {
    cin >> n >> m; work(1, 1);
    return 0;
}

递归实现排列型枚举

int n, a[9];

int main() {
    cin >> n; for (int i = 0; i < n; ++i) a[i] = i + 1, cout << i + 1 << ' ';
    for (cout << '\n';next_permutation(a, a + n); cout << '\n') 
        for (int i = 0; i < n; ++i) cout << a[i] << ' ';
    return 0;
}

费解的开关

int a[25][25], _, b[25][25], cnt, cur;

void work(int x, int y) {
    if (x) b[x - 1][y] ^= 1;
    if (x < 4) b[x + 1][y] ^= 1;
    if (y) b[x][y - 1] ^= 1;
    if (y < 4) b[x][y + 1] ^= 1;
    b[x][y] ^= 1; ++cur;
}

int main() {
    for (cin >> _, cnt = 7; _; --_, cnt = 7) {
        for (int i = 0; i < 5; ++i) for (int j = 0; j < 5; ++j) scanf("%1d", a[i] + j);
        for (int i = 0; i < (1 << 5); ++i) {
            memcpy(b, a, sizeof a); cur = 0;
            for (int j = 0; j < 5; ++j) if (i >> j & 1) work(0, j);
            for (int j = 0; j < 4; ++j) for (int k = 0; k < 5; ++k) if (!b[j][k]) work(j + 1, k);
            for (int j = 0; j < 5; ++j) if (!b[4][j]) { cur = 7; break; }
            cnt = min(cnt, cur);
        }
        cout << (cnt == 7 ? -1 : cnt) << '\n';
    }
    return 0;
}

⭐奇怪的汉诺塔

  1. 要将n个盘子移到 d, 就要先把 x 个盘子从 a 借助 b, c, d 移到b(c)上

  2. 再把剩下的(n - x)个盘子, 从 a 借助 c(b), d 移动到 d 上

  3. 再把 x 个盘子 从 b(c) 借助 a, c(b), d 移动到 d 上

第2步, 就是最普通的汉诺塔

int d[13], f[13];

int main() {
    for (int i = 1; i < 13; ++i) {
        d[i] = (d[i - 1] << 1) + 1; f[i] = 0x3f3f;
        for (int j = 0; j < i; ++j) f[i] = min(f[i], (f[j] << 1) + d[i - j]);
        cout << f[i] << '\n';
    }
    return 0;
}

⭐约数之和

用公式 \(\frac {a(q^n-1)} {q - 1}\) 主要考虑 (q - 1) % mod == 0的问题

设 q = k * mod + 1, 则

\(\frac {q^n-1} {q - 1}\) % mod = \(\frac {(kmod + 1)^n-1} {kmod - 1 + 1}\) % mod
\(= \frac {(kmod)^n + C_n^{n - 1}(kmod)^{n - 1} + ... + C_n^1(kmod) + 1 - 1} {k * mod}\) % mod
\(= [(kmod)^{n - 1} + C_n^{n - 1}(kmod)^{n - 2} + .. + C_n^1]\) % mod
\(= C_n^1\) % mod = n % mod

然后分情况讨论即可

ll a, b, ans = 1;

ll qpow(ll a, ll b) {
    ll ans = 1;
    for (; b; b >>= 1, a = a * a % mod) if (b & 1) ans = ans * a % mod;
    return ans;
}

int main() {
    cin >> a >> b; if (!a) return cout << 0, 0;
    for (int i = 2; i <= a; ++i) if (a % i == 0) {
        int cnt = 0; for (; a % i == 0; a /= i, cnt += b); ++cnt;
        if ((i - 1) % mod == 0) ans = ans * (cnt % mod) % mod;
        else ans = (qpow(i, cnt) - 1 + mod) % mod * ans % mod * qpow(i - 1, mod - 2) % mod;
    }
    cout << ans;
    return 0;
}

⭐分型之城

写过单独的题解, 就不细说了, 能搜到

就像2019的蓝桥国赛A组, 一道题填空, 一道大题都是类似这道题的

PLL work(ll id, int k) {
    if (!k) return { 1, 1 };
    ll len = 1 << k - 1, sz = len * len;
    auto cur = work(id % sz, k - 1);
    switch (id / sz) {
    case 0 : return { cur.se, cur.fi };
    case 1 : return { cur.fi + len, cur.se };
    case 2 : return { cur.fi + len, cur.se + len };
    }
    return { len + 1 - cur.se, (len << 1) + 1 - cur.fi };
}

int main() {
    for (cin >> _; _; --_) {
        ll a, b; cin >> n >> a >> b; --a, --b;
        PLL x = work(a, n), y = work(b, n);
        cout << (ll)round(sqrt(sqr(x.fi - y.fi) + sqr(x.se - y.se)) * 10) << '\n';
    }
    return 0;
}

前缀和与差分

激光炸弹

注意卡空间, 不开ll就行了

int s[5002][5002], ans;

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        int x, y, z; cin >> x >> y >> z;
        s[x + 1][y + 1] += z;
    }
    for (int i = 1; i <= 5001; ++i) for (int j = 1; j <= 5001; ++j) {
        s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
        if (i >= m && j >= m) ans = max(ans, s[i][j] - s[i - m][j] - s[i][j - m] + s[i - m][j - m]);
        else if (i >= m) ans = max(ans, s[i][j] - s[i - m][j]);
        else if (j >= m) ans = max(ans, s[i][j] - s[i][j - m]);
        else ans = max(ans, s[i][j]);
    }
    cout << ans;
    return 0;
}

IncDec序列

long long n, a[100000], x, y;

int main() {
    cin >> n; for (int i = 0; i < n; ++i) cin >> a[i];
    for (int i = n - 1; i; --i) { a[i] -= a[i - 1]; if (a[i] > 0) x += a[i]; else y -= a[i]; }
    cout << min(x, y) + abs(x - y) << '\n' << abs(y - x) + 1;
    return 0;
}

最高的牛

int n, p, h, m, s[10001];

int main() {
    cin >> n >> p >> s[0] >> m; set<pair<int, int>> v;
    for (int i = 0; i < m; ++i) {
        int a, b; cin >> a >> b; if (a > b) swap(a, b);
        if (a == b || v.count({ a, b })) continue;
        v.insert({ a, b }); --s[a + 1], ++s[b];
    }
    for (int i = 1; i <= n; ++i) cout << (s[i] += s[i - 1]) << '\n';
    return 0;
}

二分

最佳牛围栏

这样check是不对的, 不存在尺取和贪心, nlogn 就行了没必要再画蛇添足

bool check(ll mid) {
    for (int i = 1, j = m; i + m - 1 <= n; j = ++i + m - 1) {
        ll cur = s[j] - s[i - 1];
        while (j < n && cur <= a[j + 1] * (j - i + 1)) cur += a[++j];
        if (cur >= mid * (j - i + 1)) return 1;
    }
    return 0;
}
ll a[100001], b[100001];

bool check(ll mid) {
    for (int i = 1; i < m; ++i) b[i] = a[i] - mid + b[i - 1];
    for (ll i = m, mi = 0; i <= n; ++i) {
        mi = min(mi, b[i - m]); b[i] = a[i] - mid + b[i - 1];
        if (b[i] - mi >= 0) return 1; 
    }
    return 0;
}

int main () {
    cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> a[i], a[i] *= 1000;
    ll l = 1, r = 2000000;
    while (l < r) {
        ll mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    cout << l;
    return 0;
}+

⭐特殊排序

比较不好想二分, 我们保证 a[l - 1] < i && a[r + 1] > i, 然后不断缩小 l~r 就好了

最后是 r = l - 1, 及 a[r + 1] -> a[l] > i, a[l - 1] = a[r] > i 所以 i 插在 r 和 l 之间

class Solution {
public:
    vector<int> specialSort(int N) {
        vector<int> res(1, 1);
        for (int i = 2; i <= N; ++ i) {
            int l = 0, r = res.size() - 1;
            while (l <= r) {
                int mid = (l + r) >> 1;
                if (compare(res[mid], i)) l = mid + 1;
                else r = mid - 1;
            }
            res.insert(l + res.begin(), i);
        }
        return res;
    }
};

排序

电影

int n, x, y, ans, _, a[200001];
unordered_map<int, int> st;

int main() {
    for (cin >> _; _; --_) cin >> ans, ++st[ans];
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= n; ++i) {
        int cur; cin >> cur;
        if (st[a[i]] > x) ans = i, x = st[a[i]], y = st[cur];
        else if (st[a[i]] == x && st[cur] > y) ans = i, y = st[cur];
    }
    cout << ans;
    return 0;
}

仓货选址

ll n, a[100000], ans;

int main() {
    cin >> n; for (int i = 0; i < n; ++i) cin >> a[i]; sort(a, a + n);
    for (int i = 0; i < n; ++i) ans += abs(a[i] - a[n - 1 >> 1]);
    cout << ans;
    return 0;
}

⭐七夕祭

单独写过题解, 就不解释了, 环形均摊纸牌 + 仓货选址

ll work(int a[], int n) {
    for (int i = 0; i < n; ++i) s[i] = s[i - 1] + a[i] - k / n;
    sort(s, s + n); ll ans = 0;
    for (int i = 0; i < n; ++i) ans += abs(s[i] - s[n >> 1]);
    return ans;
}

int main() {
    cin >> n >> m >> k;
    for (int i = 0; i < k; ++i) {
        int a, b; cin >> a >> b;
        ++c[--b], ++r[--a];
    }
    if (k % n + k % m == 0) cout << "both " << work(c, m) + work(r, n);
    else if (k % n == 0) cout << "row " << work(r, n);
    else if (k % m == 0) cout << "column " << work(c, m);
    else cout << "impossible";
    return 0;
}

动态中位数(对顶堆)

int main() {
    for (cin >> _; _; --_) {
        int id; cin >> id >> n; cout << id << ' ' << (n + 1 >> 1);
        priority_queue<int> x, y;
        for (int i = 1; i <= n; ++i) {
            int a; cin >> a;
            if (x.size() <= y.size())
                if (!y.empty() && -y.top() < a) x.push(-y.top()), y.pop(), y.push(-a);
                else x.push(a);
            else
                if (x.top() > a) y.push(-x.top()), x.pop(), x.push(a);
                else y.push(-a);
            if (i % 20 == 1) cout << '\n';
            if (i & 1) cout << x.top() << ' ';
        }
        cout << '\n';
    }
    return 0;
}

超快速排序

找逆序对

int n, _, a[500001], c[500001];

void add(int x, int k) { for (; x <= n; x += -x & x) c[x] += k; }

int ask(int x) { int ans = 0; for (; x; x -= -x & x) ans += c[x]; return ans; }

int main() {
    while (cin >> n, n) {
        vector<int> c(n + 1, -1); ll ans = 0;
        for (int i = 1; i <= n; ++i) cin >> a[i], c[i] = a[i];
        sort(all(c)); c.erase(unique(all(c)), c.end());
        for (int i = 1; i <= n; add(a[i++], 1))
            ans += ask(n) - ask(a[i] = lower_bound(all(c), a[i]) - c.begin());
        for (int i = 1; i <= n; add(a[i++], -1));
        cout << ans << '\n';
    }
    return 0;
}

奇数码问题

int n, _, a[250000], c[250000];

void add(int x, int k) { for (; x && x < n * n; x += -x & x) c[x] += k; }

int ask(int x) { int ans = 0; for (; x; x -= -x & x) ans += c[x]; return ans; }

int main() {
    while (cin >> n) {
        int x = 0;
        for (int i = 0; i < n * n; add(a[i++], 1)) {
            cin >> a[i]; if (!a[i]) continue;
            x += ask(n * n - 1) - ask(a[i]);
        }
        for (int i = 0; i < n * n; add(a[i++], -1));
        for (int i = 0; i < n * n; add(a[i++], 1)) {
            cin >> a[i]; if (!a[i]) continue;
            x -= ask(n * n - 1) - ask(a[i]);
        }
        for (int i = 0; i < n * n; add(a[i++], -1));
        cout << (x & 1 ? "NIE\n" : "TAK\n");
    }
    return 0;
}

倍增

⭐天才ACM

书上复杂度, 我是没理解, 怎么算也是 \(nlog^2n\), 排序复杂度的上线是死的, 除非用桶排,计数才能降到 \(Nlogn\),

但是注意到倍增每次sort的是倍增的一端, sort排序的很少, 达不到上界\(nlog^2n\)

N是数据范围, 还不如sort...

int n, _, m, a[N], b[N], c[N];
ll t;

bool check(int l, int mid, int r) {
    memcpy(b + mid + 1, a + mid + 1, (r - mid) * sizeof(int)); sort(b + mid + 1, b + r + 1);
    ll ans = 0; int i = l, j = mid + 1, k = l;
    while (i <= mid && j <= r) c[k++] = b[i] <= b[j] ? b[i++] : b[j++];
    while (i <= mid) c[k++] = b[i++];
    while (j <= r) c[k++] = b[j++];
    for (int i = 0; i < m && 2 * i < r - l; ++i) ans += sqr((ll)c[r - i] - c[l + i]);
    return ans <= t ? memcpy(b + l, c + l, (r - l + 1) * sizeof(int)), 1 : 0;
}

int main() {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    for (cin >> _; _; --_) {
        cin >> n >> m >> t;
        for (int i = 1; i <= n; ++i) cin >> a[i], b[i] = a[i];
        int l = 1, r = 1, cnt = 0, len = 1;
        while (l <= n) {
            while (len && r < n) {
                if (check(l, r, min(r + len, n))) r += len, len <<= 1;
                else if (r + len > n)len = 1;
                else len >>= 1;
            }
            ++cnt; l = ++r; len = 1;
        }
        cout << cnt << '\n';
    }
    return 0;
}

贪心

⭐防晒

贪心的题, 硬生生被我做成了最大匹配

按minSPF降序排列, 按顺序每头牛选能用的SPF最大的防晒霜

要以后某头牛i能用它前面牛j用的防晒霜, 那前面那头也找不到能用的了,

毕竟 minSPF[i] <= minSPF[j], minSPF[i] ~ useSPF[j] 之间没有防晒霜能用了

也就是 minSPF[j] ~ usePSF[j] 之间没有让 j 用的(除了它将要用的那瓶, 毕竟 useSPF[j] ~ maxSPF[j] 之间没有能用的, 不然 useSPF[j] 就选错了)

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i) cin >> a[i].fi >> a[i].se;
    for (int i = 0; i < m; ++i) { int x, c; cin >> x >> c; b[x] += c; }
    sort(a, a + n, greater<pair<int, int>>());
    for (int i = 0; i < n; ++i)
        for (int j = a[i].se; j >= a[i].fi; --j) if (b[j] > 0) {++ans, --b[j]; break; }
    cout << ans;
    return 0;
}

畜栏预定

int n, rk[50000];
pair<int, int> a[50000];

int main() {
    cin >> n; for (int i = 0; i < n; ++i) cin >> a[i].se >> a[i].fi, rk[i] = i;
    sort(rk, rk + n, [&](int x, int y) { return a[x].fi < a[y].fi; });
    set<pair<int, int>> st; st.insert({ 0, 1 });
    for (int i = 0; i < n; ++i)
        if (st.begin()->fi >= a[rk[i]].se) st.insert({ a[rk[i]].fi, a[rk[i]].se = st.size() + 1 });
        else {
            auto it = st.lower_bound({ a[rk[i]].se, 0}); --it;
            a[rk[i]].se = it->se; st.erase(it); st.insert(a[rk[i]]);
        }
    cout << st.size() << '\n';
    for (int i = 0; i < n; ++i) cout << a[i].se << '\n';
    return 0;
}

⭐雷达设备

很经典的题

int n, m, sq, ans;
pair<double, double> s[1005];
double cur = -1e10;

int main(){
    cin >> n >> m; sq = m * m;
    for (int i = 0; i < n; ++i) {
        int a, b; cin >> a >> b;
        if (b > m) return cout << -1, 0;
        double d = sqrt(sq - b * b);
        s[i] = { a + d, a - d };
    }
    sort(s, s + n);
    for (int i = 0; i < n; ++i) if (cur < s[i].second) ++ans, cur = s[i].first;
    return cout << ans, 0;
}

国王游戏

卡高精是真的恶心

#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> PII;

const int N = 1010;

int n;
PII p[N];

vector<int> mul(vector<int>a, int b){
    vector<int> c; int t = 0;
    for (int i = 0; i < a.size(); ++i) t += a[i] * b, c.push_back(t % 10), t /= 10;
    while (t) c.push_back(t % 10), t /= 10;
    return c;
}

vector<int> div(vector<int>a, int b) {
    vector<int> c; bool is_first = true;
    for (int i = a.size() - 1, t = 0; i >= 0; --i, t %= b){
        t = t * 10 + a[i]; int x = t / b;
        if (!is_first || x) is_first = false, c.push_back(x);
    }
    reverse(c.begin(), c.end());
    return c;
}

vector<int> max_vec(vector<int> a, vector<int> b){
    if (a.size() > b.size()) return a;
    if (a.size() < b.size()) return b;
    if (vector<int>(a.rbegin(), a.rend()) > vector<int>(b.rbegin(), b.rend())) return a;
    return b;
}

int main(){
    cin >> n;
    for (int i = 0; i <= n; ++i){
        int a, b; cin >> a >> b;
        p[i] = {a * b, a};
    }
    sort(p + 1, p + n + 1);
    vector<int> product(1, 1), res(1, 0);
    for (int i = 0; i <= n; ++i){
        if (i) res = max_vec(res, div(product, p[i].first / p[i].second));
        product = mul(product, p[i].second);
    }
    for (int i = res.size() - 1; ~i; --i) cout << res[i];
    return 0;
}

⭐给树染色

对于非根节点(a->b, c)

  • 先染色a,b 再染色 c : a + 2b + 3c
  • 先染色c 再染色 a,b : c + 2a + 3b

同时分别+(c-b),再除以2 则

  • \(\frac{a+b}{2} + 2 * c\)
  • \(c + 2 * \frac{a+b}{2}\)

进一步推广,如果有两组点:\(a1,a2,…an\)\(b1,b2,…bm\),组内的点在染色时是相邻的一段。我们现在来考虑何时应该先染第一组点:

  • 如果先染\(ai\),则分值是 \(S_{ab}=∑^n_{i=1}a_i∗i+∑^{n+m}_{i=n+1}b_{i-n}∗i\)
  • 如果先染\(bi\),则分值是 \(S_{ba}=∑^m_{i=1}b_i∗i+∑^{n+m}_{i=m+1}a_{i-m}∗i\)

\(S_{ab}−S_{ba}=n∗∑^m_{i=1}b_i−m∗∑^n_{i=1}a_i\),所以\(S_{ab}−S_{ba}<0⟺\frac{∑^n_{i=1}a_i}{n}<\frac{∑^m_{i=1}b_i}{m}\)

所以我们在考虑剩余点的染色顺序时,可以将这两组点分别当成两个点,其权值分别是两组内所有点权值的平均值。

然后不断取当前点集的最大权值染色并合并父亲即可。

vector<int> z[1005];

struct di { int d, p, v; double ave; } tr[1005];

int find() {
    int w; double ans = -1;
    for (int i = 1; i <= n; ++i) if (tr[i].d > -1 && i != m)
        if (tr[i].ave > ans) ans = tr[i].ave, w = i;
    return w;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> tr[i].v, tr[i].d = 1, ans += tr[i].v, tr[i].ave = tr[i].v;
    for (int i = 1, a, b; i < n; ++i) cin >> a >> b, tr[b].p = a, z[a].emplace_back(b);
    for (int i = 1; i < n; ++i) {
        int k = find(), p = tr[k].p;
        ans += tr[p].d * tr[k].v;
        for (int j = 0; j < z[k].size(); ++j) tr[z[k][j]].p = p, z[p].emplace_back(z[k][j]);
        z[k].resize(0); tr[p].d += tr[k].d; tr[p].v += tr[k].v;
        tr[p].ave = (double)tr[p].v / tr[p].d; tr[k].d = -1;
    }
    cout << ans;
    return 0;
}

总结与练习

飞行员兄弟

int read() { char c = 'a'; while (c != '-' && c != '+') c = getchar(); return c == '-'; }

void move(int x, int y) { for (int i = x, j = y; i < 16; i += 4) b[i] ^= 1, b[j++] ^= 1; }

int main()  {
    for (int i = 0; i < 16; ++i) a[i] = read();
    for (int i = 1; i < (1 << 16); ++i)  {
        int cnt = 0; memcpy(b, a, sizeof a);
        for (int j = 0; j < 16; ++j) if (i >> j & 1) b[j] ^= 1, move(j % 4, j / 4 * 4), ++cnt;
        for (int j = 0; j < 16; ++j) if (!b[j]) { cnt = 17; break; }
        if (cnt < ans) ans = cnt, res = i;
    }
    printf("%d", ans);
    for (int j = 0; j < 16; ++j) if (res >> j & 1) printf("\n%d %d", j / 4 + 1, j % 4 + 1);
    return 0;
}

占卜DIY

inline int read() {
    char c = getchar(); int x = 0;
    while (!isdigit(c) && c != 'K' && c != 'J' && c != 'Q' && c != 'A') c = getchar();
    switch (c) {
    case 'K' : return 13;
    case 'Q' : return 12;
    case 'J' : return 11;
    case 'A' : return 1;
    case '0' : return 10;
    default : return c ^ '0';
    }
}

int main() {
    for (int i = 1; i <= 13; ++i) for (int j = 1; j <= 4; ++j) a[i][j] = read();
    for (; life; s == 0) {
        while ((s == 13 || !s) && life) {
            for (int i = 1; i <= 4; ++i) if (a[13][i]) { ++b[s = a[13][i]], a[13][i] = 0; break; }
            if (s == 13) --life;
        }
        while (s != 13 && life) {
            for (int i = 4, kk; i && s != 13 && life; --i) if (a[s][i]) {
                kk = s; s = a[s][i]; a[kk][i] = 0; ++b[s]; break;
            }
            if (s == 13) --life;
        }
    }
    for (int i = 1; i <= 12; ++i) if (b[i] == 4) ++ans;
    return printf("%d", ans), 0;
}

分形

int n, inc[7] = {1}, s[730][730];

void work(int k, int x, int y) {
    if (k == 1) { s[x][y] = 1; return; }
    work(k - 1, x, y); work(k - 1, x, y + (inc[k - 2] << 1));
    work(k - 1, x + inc[k - 2], y + inc[k - 2]);
    work(k - 1, x + (inc[k - 2] << 1), y);
    work(k - 1, x + (inc[k - 2] << 1), y + (inc[k - 2] << 1));
}

int main() {
    for (int i = 1; i < 7; ++i) inc[i] = inc[i - 1] * 3;
    work(7, 0, 0);
    while (cin >> n, n > 0) {
        for (int i = 0; i < inc[n - 1]; ++i, cout << '\n')
            for (int j = 0; j < inc[n - 1]; ++j) cout << (s[i][j] ? 'X' : ' ');
        cout << "-\n";
    }
    return 0;
}

⭐袭击(二维最近点对)

struct node { 
    double x, y; bool k;
    double dist(const node& a) { return k == a.k ? 2e9 : sqrt(sqr(x - a.x) + sqr(y - a.y)); }
} a[200005], b[200005];

double work(int l, int r) {
    if (r - l <= 1) return 2e9;
    int mid = l + r >> 1, x = a[mid].x;
    double cur = min(work(l, mid), work(mid, r));
    int i = l, j = mid, k = l;
    while (i < mid && j < r) b[k++] = a[i].y <= a[j].y ? a[i++] : a[j++];
    while (i < mid) b[k++] = a[i++];
    while (j < r) b[k++] = a[j++];
    for (i = l, k = 0; i < r; a[i] = b[i], ++i)
        if (a[i].x > x - cur && a[i].x < x + cur) b[k++] = a[i];
    for (i = 0; i < k; ++i) for (j = i - 1; j >= 0 && b[i].y - b[j].y < cur; --j)
        cur = min(cur, b[i].dist(b[j]));
    return cur;
}

int main() {
    for (cin >> _; _; --_) {
        cin >> n;
        for (int i = 0; i < n; ++i) cin >> a[i].x >> a[i].y, a[i].k = 1;
        for (int i = 0; i < n; ++i) cin >> a[i + n].x >> a[i + n].y;
        sort(a, a + (n << 1), [&](node& x, node& y) { return x.x < y.x; });
        cout << setiosflags(ios::fixed) << setprecision(3) << work(0, n << 1) << '\n';
    }
    return 0;
}

防线

int check(int mid) {
    int f = 0;
    for (int i = 0; i < n; ++i) if (mid >= s[i])
        f += (min(mid, e[i]) - s[i]) / d[i] + 1;
    return f;
}

int main() {
    for (scanf("%d", &_); _; --_) {
        scanf("%d", &n);
        for (int i = 0; i < n; ++i) scanf("%d%d%d", s + i, e + i, d + i);
        long long l = 0, r = (long long)1 << 31;
        while (l < r) {
            long long mid = l + r >> 1;
            if (check(mid) & 1) r = mid;
            else l = mid + 1;
        }
        if (l < (long long)1 << 31) printf("%d %d\n", l, check(l) - check(l - 1));
        else puts("There's no weakness.");
    }
    return 0;
}

赶牛入圈

想着下标从1开始加个最小值, 结果因为找到了这个最小值越界了...

int n, c, a[501][501];
pair<int, int> p[501];
vector<int> x(1, -1), y(1, -1);

int find(vector<int>& x, int a) { return lower_bound(all(x), a) - x.begin(); }

bool check(int mid) {
    per(x2, x.size() - 1, 1)
        per(y2, y.size() - 1, 1) {
            int y1 = max(find(y, y[y2] - mid + 1), 1), x1 = max(1, find(x, x[x2] - mid + 1));
            if (a[x2][y2] + a[x1 - 1][y1 - 1] - a[x1 - 1][y2] - a[x2][y1 - 1] >= c) return 1;
        }
    return 0;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> c >> n;
    rep(i, 1, n) cin >> p[i].fi >> p[i].se, x.push_back(p[i].fi), y.push_back(p[i].se);
    sort(all(x)); x.erase(unique(all(x)), x.end());
    sort(all(y)); y.erase(unique(all(y)), y.end());
    rep(i, 1, n) ++a[p[i].fi = find(x, p[i].fi)][p[i].se = find(y, p[i].se)];
    rep(i, 1, x.size() - 1) rep(j, 1, y.size() - 1)
        a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
    int l = 1, r = 10000;
    while (l < r) {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    cout << r;
    return 0;
}

糖果传递

int main() {
    cin >> n; for (int i = 1; i <= n; ++i) cin >> s[i], a += s[i];
    a /= n; for (int i = 1; i <= n; ++i) s[i] += s[i - 1] - a;
    sort(s + 1, s + 1 + n); a = 0;
    for (int i = 1; i <= n; ++i) a += abs(s[i] - s[n >> 1]);
    cout << a;
    return 0;
}

⭐士兵

均摊纸牌, 但是对于x轴, 是移动后相邻
对x排序, 假设最后最右边位置mx(\(mx >= max_x\)), 则\(x_i相对x_n少走i-1步\), 即x[i] -= i - 1
在对x排序做均摊纸牌

int main() {
    cin >> n; for (int i = 0; i < n; ++i) cin >> x[i] >> y[i];
    sort(x, x + n); sort(y, y + n);
    for (int i = 0; i < n; ++i) x[i] -= i; sort(x, x + n);
    for (int i = 0; i < n; ++i) ans += abs(y[i] - y[n >> 1]) + abs(x[i] - x[n >> 1]);
    cout << ans;
    return 0;
}

⭐数的进制转换

int _, n, m;
string s;

void work(int a, int b, string& s) {
    int len = int(s.size()), k; vector<int> cur(len), ans;
    per (i, len - 1, 0) cur[len - i - 1] = s[i] - (s[i] < 58 ? 48 : s[i] < 97 ? 55 : 61);
    while(len) {
        per (i, len - 2, 0) cur[i] += cur[i + 1] % b * a, cur[i + 1] /= b;
        ans.push_back(cur[0] % b); cur[0] /= b;
        while (len && !cur[len - 1]) --len;
    }
    string(ans.size(), ' ').swap(s);
    rep (i, 0, ans.size() - 1)
        s[ans.size() - 1 - i] = char(ans[i] + (ans[i] <= 9 ? 48 : ans[i] <= 35 ? 64 - 9 : 96 - 35));
}

int main() {
    for (cin >> _; _; --_) {
        cin >> n >> m >> s; cout << n << ' ' << s << '\n';
        work(n, m, s); cout << m << ' ' << s << '\n' << '\n';
    }
    return 0;
}

耍杂技的牛

int main() {
    cin >> n; for (int i = 0; i < n; ++i) cin >> a[i].fi >> a[i].se, a[i].fi += a[i].se;
    sort(a, a + n); for (int i = 0; i < n; ++i) ans = max(ans, s - a[i].se), s += a[i].fi - a[i].se;
    cout << ans;
    return 0;
}

最大的和

int main() {
    cin >> n;
    rep (i, 1, n) rep (j, 1, n) cin >> h[i][j], ans = max(ans, h[i][j]), h[i][j] += h[i][j - 1];
    rep (i, 0, n - 1) for (int j = i + 1, res = 0; j <= n; ++j, res = 0) rep (k, 1, n)
        if (h[k][i] - h[k][j] >= res) res = 0;
        else ans = max(ans, res += h[k][j] - h[k][i]);
    return cout << ans, 0;
}

任务

multiset<int> st[101];
int main() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i) cin >> a[0].fi >> a[0].se, st[a[0].se].insert(a[0].fi);
    for (int i = 0; i < m; ++i) cin >> a[i].fi >> a[i].se; sort(a, a + m);
    for (int i = m - 1; ~i; --i) {
        for (int j = a[i].se; j < 101; ++j) {
            auto it = st[j].lower_bound(a[i].fi);
            if (it == st[j].end()) continue;
            ans += a[i].fi * 500 + a[i].se * 2; ++cnt;
            st[j].erase(it); break; 
        }
    }
    cout << cnt << ' ' << ans;
    return 0;
}

基本数据结构

包含min函数的栈

class MinStack {
public:
    stack<int> st;
    vector<int> mi;
    
    void push(int x) {
        st.push(x);
        if (mi.empty()) mi.push_back(x);
        else mi.push_back(min(x, mi.back()));
    }
    
    void pop() { st.pop(); mi.pop_back(); }
    
    int top() { return st.top(); }
    
    int getMin() { return mi.back(); }
};

编辑器

vector<pair<int, pair<int,int>>> st;
stack<int> qu;

void work(int a) {
    int b = st.empty() ? 0 : a + st.back().se.se;
    if (st.empty()) st.pb({a, {a, a}});
    else st.pb({a, {max(st.back().se.fi, b), b}});
}

int main(){
    for (cin >> _; _; --_) {
        char c; cin >> c;
        switch (c) {
        case 'I' : cin >> n; work(n); break;
        case 'D' : if (!st.empty()) st.pop_back(); break;
        case 'L' : if (!st.empty()) qu.push(st.back().fi), st.pop_back(); break;
        case 'R' : if (!qu.empty()) work(qu.top()), qu.pop(); break;
        default : cin >> n; cout << st[n - 1].se.fi << '\n';
        }
    }
}

火车进栈

卡特兰数的高精

vector<long long> a(1, 1);

void getprime(int n, int k) {
    for (int i = 2; i <= n / i; ++i)
        while (n % i == 0) c[i] += k, n /= i;
    c[n] += k;
}

void mul(int n) {
    for (auto &i : a) i *= n;
    for (int i = 0; i < a.size() - 1; ++i) a[i + 1] += a[i] / mod, a[i] %= mod;
    if (a.back() >= mod) a.push_back(a.back() / mod), a[a.size() - 2] %= mod;
}

int main() {
    cin >> n; getprime(2, 1);
    for (int i = n + 2; i < n << 1; ++i) getprime(i, 1);
    for (int i = 2; i < n; ++i) getprime(i, -1);
    for (int i = 2; i < n << 1; ++i) for (int j = 0; j < c[i]; ++j) mul(i);
    cout << a.back(); cout.fill('0');
    for (int i = a.size() - 2; ~i; --i) cout << setw(9) << a[i];
    return 0;
}

直方图中最大的矩形

int main() {
    while (cin >> n, n) {
        stack<pair<int, int>> st; st.push({ans = 0, h[n + 1] = 0});
        for (int i = 1, w = 1; i <= n + 1; st.push({w, i++}), w = 1) {
            if (i <= n) cin >> h[i];
            for (auto p = st.top(); h[i] < h[p.second]; st.pop(), p = st.top()) {
                ans = max(ans, 1ll * (p.first + i - p.second - 1) * h[p.second]);
                w += p.first;
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

队列

小组队列

vector<queue<int>> q;
int t, n, a, f[1000010], cas;
char op[10];

int main() {
    while (cin >> t, t) {
        vector<queue<int>>(t + 1).swap(q); cout << "Scenario #" << ++cas << '\n';
        for (int i = 0; i < t; ++i) for (cin >> n; n; --n) cin >> a, f[a] = i;
        while (cin >> op, op[0] != 'S')
            switch (op[0]) {
            case 'E' : cin >> a; q[f[a]].push(a); 
                if (q[f[a]].size() == 1) q[t].push(f[a]); break;
            case 'D' : a = q[t].front(); cout << q[a].front() << '\n'; q[a].pop();
                if (q[a].empty()) q[t].pop(); break;
            }
        puts("");
    }
}

蚯蚓

优先队列复杂度炸掉, 考虑贪心

对于两条相邻切割的蚯蚓长度为\(x_1, x_2(x_1 >= x_2)\)

切割完\(x_2\)后, 生成的四条蚯蚓的长度为

\(l_1 = \left \lfloor x_1 * p \right \rfloor + q, r_1 = x_1 - l_1 + q\)

\(l_2 = \left \lfloor (x_2 + q) * p \right \rfloor, r_2 = x_2 + q - l_2\)

因为 \(x_1 >= x_2, 0 < p < 1\)\(l_1 > l_2\)

\(r_1 - r_2 = (x_1 - l_1) - (x_2 - l_2)\)
\(= \left \lceil x_1 - x_1 * p \right \rceil - \left \lceil x_2 - l_2 * p \right \rceil\)
\(= \left \lceil x_1 * (1 - p) \right \rceil - \left \lceil x_2 * (1 - p) \right \rceil >= 0 (x_1 >= x_2)\)

所以存在\(x_1 >= x_2\) 必存在 \(l_1 > l_2, r_1 >= r_2\)

所以分3个队列, 存x, l, r, 每次从3个队列取最大值, 切割分别放入l, r的队尾

queue<int> x, l, r;

int calc(int t) {
    int d = -2e9, a = -2e9, b = -2e9, c = -2e9;
    if (!x.empty()) d = max(d, a = x.front());
    if (!l.empty()) d = max(d, b = l.front());
    if (!r.empty()) d = max(d, c = r.front());
    if (d == a) x.pop();
    else if (d == b) l.pop();
    else r.pop();
    return d + t * q;
}

int main() {
    cin >> n >> m >> q >> u >> v >> t;
    for (int i = 0; i < n; ++i) cin >> a[i]; sort(a, a + n);
    for (int i = n - 1; ~i; --i) x.push(a[i]);
    for (int i = 1; i <= m; ++i) {
        int x = calc(i - 1), y = x  * (long long) u / v, c = i * q; 
        if (!(i % t)) cout << x << " ";
        l.push(y - c); r.push(x - y - c);
    }
    cout << endl;
    for (int i = 1; i <= n + m; ++i) {
        int x = calc(m);
        if (i % t == 0) cout << x <<" ";
    }
}

⭐双端队列

int main() {
    int n, m = 1; cin >> n; vector<int> a(n);
    unordered_map<int, pair<int, int>> st;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        if (st.count(a[i])) st[a[i]].second = i;
        else st[a[i]] = {i, i};
    }
    sort(a.begin(), a.end()); a.erase(unique(a.begin(), a.end()), a.end());
    for (int i = 1, j = -st[a[0]].first; i < a.size(); ++i)
        if (j < 0 && st[a[i]].second < -j) j = -st[a[i]].first;
        else if (j < 0 || j < st[a[i]].first) j = st[a[i]].second;
        else ++m, j = -st[a[i]].first;
    cout << m;
    return 0;
}

⭐最大子序和

int main() {
    int n, m; cin >> n >> m; vector<long long> a(n + 1);
    for (int i = 1; i <= n; ++i) cin >> a[i], a[i] += a[i - 1];
    long long ans = -2e18; deque<pair<long long, int>> q; q.push_back({0, 0});
    for (int i = 1; i <= n; ++i) {
        while (q.front().second + m < i) q.pop_front();
        ans = max(ans, a[i] - q.front().first);
        while (!q.empty() && q.back().first >= a[i]) q.pop_back();
        q.push_back({a[i], i});
    }
    cout << ans;
    return 0;
}

链表与邻接表

临值查找

int main() {
    int n, m; cin >> n >> m; set<pair<int, int>> st; st.insert({m, 1});
    for (int i = 2; i <= n; st.insert({m, i++})) {
        cin >> m; auto it = st.lower_bound({m, 0});
        if (it == st.end()) --it, cout << abs(it->first - m) << ' ' << it->second << '\n';
        else if (it == st.begin()) cout << abs(it->first - m) << ' ' << it->second << '\n';
        else {
            auto p = *it; --it;
            if (abs(it->first - m) <= abs(p.first - m)) cout << abs(it->first - m) << ' ' << it->second << '\n';
            else cout << abs(p.first - m) << ' ' << p.second << '\n';
        }
    }
    return 0;
}

Hash

雪花雪花雪花

const int p[2] = { 131, 13331 };

int n, a[6], b[6];
bool f = 1;
set<pair<ull, ull>> st;

void findmx() {
    memcpy(b, a, sizeof a);
    int i = 0, j = 1, k = 0;
    while (i < 6 && j < 6 && k < 6)
        if (b[(i + k) % 6] == b[(j + k) % 6]) ++k;
        else {
            if (b[(i + k) % 6] < b[(j + k) % 6]) j += k + 1;
            else i += k + 1;
            if (i == j) ++j; k = 0;
        }
    j = min(i, j);
    for (int i = 0; i < 6; ++i) a[i] = b[(i + j) % 6];
}

void work() {
    if (!f) return; ull cur[2] = { 0, 0 };
    for (int j = 0; j < 6; ++j) for (int j = 0; j < 2; ++j) cur[j] = cur[j] * p[j] + a[j];
    f = !st.count({ cur[0], cur[1] }); st.insert({ cur[0], cur[1] });
}

int main() {
    cin >> n;
    for (int i = 0; f && i < n; ++i) {
        for (int j = 0; j < 6; ++j) cin >> a[j];
        findmx(); work(); reverse(a, a + 6);
        findmx();  work();
    }
    cout << (f ? "No two snowflakes are alike." : "Twin snowflakes found.");
    return 0;
}

兔子与兔子

ull a[2][N], b[2][N] = {{1}, {1}};
char s[N];

pair<ull, ull> find(int l, int r) {
    return {a[0][r] - a[0][l] * b[0][r - l], a[1][r] - a[1][l] * b[1][r - l]};
}

int main() {
    cin >> s + 1;
    for (int i = 1; s[i]; ++i) for (int j = 0; j < 2; ++j)
        a[j][i] = a[j][i - 1] * p[j] + s[i], b[j][i] = b[j][i - 1] * p[j];
    for (cin >> _; _; --_) {
        int x, y, a, b; cin >> x >> y >> a >> b;
        cout << (find(x - 1, y) == find(a - 1, b) ? "Yes\n" : "No\n");
    }
    return 0;
}

回文子串的最大长度

struct PAM {
    static const int N = 1e6 + 5, M = 26, C = 'a';
    struct Node { int ne[M], len, fail, cnt; } tr[N];
    int sz, tot, last, mx;
    char s[N];
    int newNode(int l) {
        memset(tr[++sz].ne, 0, sizeof(tr[0].ne)); tr[sz].len = l;
        return mx = max(mx, l), tr[sz].fail = tr[sz].cnt = 0, sz;
    }
    void init() {
        sz = -1; last = mx = 0; s[tot = 0] = '$';
        newNode(0); newNode(-1); tr[0].fail = 1;
    }
    int getfail(int x) {
        while (s[tot - tr[x].len - 1] != s[tot]) x = tr[x].fail;
        return x;
    }
    void insert(char c) {
        s[++tot] = c; int now = getfail(last), ch = c - C;
        if (!tr[now].ne[ch]) {
            int x = newNode(tr[now].len + 2);
            tr[x].fail = tr[getfail(tr[now].fail)].ne[ch];
            tr[now].ne[ch] = x;
        }
        ++tr[last = tr[now].ne[ch]].cnt;
    }
    void build(string& s) {
        for (int i = 0; i < s.size(); ++i) insert(s[i]);
        per (i, sz, 0) tr[tr[i].fail].cnt += tr[i].cnt;
    }
} pam;

string s;

int main() {
    int cas = 0;
    while (cin >> s, s != "END") {
        pam.init(); pam.build(s);
        cout << "Case " << ++cas << ": " << pam.mx << '\n';
    }
    return 0;
}

后缀数组

struct SA {
    static const int N = 300000 + 9;
    char str[N]; //sa[i]表示排名i的后缀起始下标,rk[i]表示起始下标i后缀的排名
    int sa[N], rk[N], tp[N], tax[N], lcp[N], len, M;
    inline void sort() {
        memset(tax, 0, (M + 1) * sizeof(int));
        rep (i, 1, len) ++tax[rk[i]];
        rep (i, 1, M) tax[i] += tax[i - 1];
        per (i, len, 1) sa[tax[rk[tp[i]]]--] = tp[i];
    }
    void getH() {
        for (int i = 1, j, k = 0; i <= len; lcp[rk[i++]] = k) {
            if (k) --k; j = sa[rk[i] - 1];
            while (str[i + k] == str[j + k]) ++k;
        }
    }
    void SuffixSort() { //字符串下标从1开始
        M = 200; len = 1;
        for (int& i = len; str[i]; ++i) rk[i] = str[i], tp[i] = i;
        --len; sort();
        for (int w = 1, p = 0; p < len; w <<= 1, M = p) {
            p = 0;
            rep (i, 1, w) tp[++p] = len - w + i;
            rep (i, 1, len) if (sa[i] > w) tp[++p] = sa[i] - w;
            sort(); swap(tp, rk); rk[sa[1]] = p = 1;
            rep (i, 2, len)
                rk[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]]
                    && tp[sa[i - 1] + w] == tp[sa[i] + w]) ? p : ++p;
        } getH();
    }
} sa;

int main() {
    cin >> sa.str + 1; sa.SuffixSort();
    rep (i, 1, sa.len) cout << sa.sa[i] - 1 << ' '; cout << '\n';
    rep (i, 1, sa.len) cout << sa.lcp[i] << ' ';
    return 0;
}

字符串

周期

struct KMP {
    static const int N = 1e6 + 5;
    int f[N];
    void kmp(char *t) {
        for (int i = 2, j = 0; t[i]; ++i) {
            while (j && t[i] != t[j + 1]) j = f[j];
            f[i] = t[i] == t[j + 1] ?  ++j : j;
        }
    }
} K;

char s[1000005];

int main() {
    int cas = 0, n;
    while (cin >> n, n) {
        cin >> s + 1; K.kmp(s); cout << "Test case #" << ++cas << "\n";
        for (int i = 1; s[i]; ++i) if (!(i % (i - K.f[i])) && K.f[i])
            cout << i << ' ' << i / (i - K.f[i]) << '\n';
        cout << '\n';
    }
    return 0;
}

Trie

前缀统计

struct AC {
    static const int N = 1e6 + 5, M = 26, C = 'a'; //字符串总长度, 字符范围
    int trie[N][M], cnt[N], tot;
    void init() { rep (i, 0, M - 1) trie[0][i] = 0; tot = 0; }
    int newnode() {
        cnt[++tot] = 0; memset(trie[tot], 0, sizeof trie[tot]);
        return tot;
    }
    void insert(char* s, int id) {
        int p = 0;
        for (int i = 0, ch = s[i] - C; s[i]; p = trie[p][ch], ch = s[++i] - C)
            if (!trie[p][ch]) trie[p][ch] = newnode();
        ++cnt[p];
    }
    int query(char* s) {
        int res = 0;
        for (int i = 0, p = trie[0][s[i] - C]; p && s[i]; p = trie[p][s[++i] - C]) res += cnt[p];
        return res;
    }
} ac;

char s[1000005];

int main() {
    int n, m; cin >> n >> m; ac.init();
    while (n--) cin >> s, ac.insert(s, n);
    while (m--) cin >> s, cout << ac.query(s) << '\n';;
    return 0;
}

最大异或对

struct AC {
    static const int N = 1e5 * 30 + 5, M = 2;
    int tr[N][M], tot;
    int insert(int x) {
        int ans = 0, p = 0, q = 0;
        for (int i = 30; ~i; --i) {
            bool ch = x >> i & 1;
            p = tr[p][ch] ? tr[p][ch] : tr[p][ch] = ++tot;
            q = tr[q][!ch] ? ans ^= (1 << i), tr[q][!ch] : tr[q][ch];
        }
        return ans;
    }
} ac;

int main() {
    int n, m, ans = 0; cin >> n;
    for (int i = 0; i < n; ++i) cin >> m, ans = max(ans, ac.insert(m));
    cout << ans;
    return 0;
}

⭐最长异或值路径

先考虑简单的线性问题, 找到一段xor和最大

设x[i] 表示 1~i 的异或和, 则答按就变成了 "最大异或对"

那树上怎么办呢, x[i] 表示从 root~i 的异或和, 找最大异或对即可

struct AC {
    static const int N = 1e5 * 30 + 5, M = 2;
    int tr[N][M], tot;
    int insert(int x) {
        int ans = 0, p = 0, q = 0;
        for (int i = 30; ~i; --i) {
            bool ch = x >> i & 1;
            p = tr[p][ch] ? tr[p][ch] : tr[p][ch] = ++tot;
            q = tr[q][!ch] ? ans ^= (1 << i), tr[q][!ch] : tr[q][ch];
        }
        return ans;
    }
} ac;

vector<pair<int, int>> h[100001];
int a[100001];

void dfs(int x, int f) {
    for (auto &y : h[x]) if (y.first != f) a[y.first] = a[x] ^ y.second, dfs(y.first, x);
}

int main() {
    int n, ans = 0; cin >> n;
    for (int i = 1; i < n; ++i) {
        int u, v, c; cin >> u >> v >> c;
        h[u].push_back({v, c}); h[v].push_back({u, c});
    }
    dfs(0, -1);
    for (int i = 0; i < n; ++i) ans = max(ans, ac.insert(a[i]));
    cout << ans;
    return 0;
}

二叉堆

超市

void add(int x, int k) { for (; x <= n; x += -x & x) c[x] += k; }

int ask(int x) {
    int ans = 0;
    for (; x; x -= -x & x) ans += c[x];
    return ans;
}

int main() {
    while (cin >> n) {
        memset(c, 0, sizeof c);
        for (int i = 0; i < n; ++i) cin >> a[i].first >> a[i].second;
        sort(a, a + n, greater<pair<int, int>>());
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            int cur = ask(a[i].second);
            if (cur == a[i].second) continue;
            int l = 1, r = a[i].second + 1;
            while (l < r) {
                int mid = l + r >> 1;
                int res = ask(mid);
                if (cur - res == a[i].second - mid) r = mid;
                else l = mid + 1;
            }
            ans += a[i].first; add(l, 1);
        }
        cout << ans << '\n';
    }
    return 0;
}

⭐序列

void work() {
    priority_queue<pair<int, int>> qu;
    for (int i = 0; i < n; i++) qu.push(make_pair(-a[0] - b[i],0));
    for (int i = 0; i < n; i++) {
        auto p = qu.top(); qu.pop(); c[i] = -p.first;
        qu.push({ p.first - a[p.second + 1] + a[p.second], p.second + 1 });
    }
    memcpy(a, c, sizeof(int) * n);
}

int main() {
    for (cin >> _; _; --_) {
        cin >> m >> n;
        for (int i = 0; i < n; i++) cin >> a[i]; sort(a, a + n);
        for (int i = 1; i < m; i++, work())
            for (int j = 0; j < n; j++) cin >> b[j], sort(b, b + n);
        for (int i = 0; i < n; i++) cout << a[i] << ' '; cout << '\n';
    }
    return 0;
}

数据备份

int main() {
    cin >> n >> k >> a[1]; priority_queue<pair<int, int>> q;
    for (int i = 2; i <= n; ++i) cin >> a[i], a[i - 1] = a[i] - a[i-1];
    a[0] = a[n] = 2e18;
    for (int i = 1; i < n; ++i) q.push({-a[i], i}), pre[i] = i - 1, nxt[i] = i + 1;
    while (k--) {
        int x = q.top().second; q.pop();
        while (del[x]) x = q.top().second, q.pop();
        ans += a[x]; del[pre[x]] = del[nxt[x]] = 1;
        a[x] = a[pre[x]] + a[nxt[x]] - a[x];
        pre[x] = pre[pre[x]]; nxt[x] = nxt[nxt[x]];
        nxt[pre[x]] = pre[nxt[x]] = x; q.push({-a[x], x});
    }
    cout << ans;
}

合并果子

int main() {
    int n, m, sum = 0; cin >> n; priority_queue<int> q;
    for (int i = 0; i < n; i++) cin >> m, q.push(-m);
    for (int i = 1; i < n; i++) {
        int x = q.top(); q.pop(); x += q.top(); q.pop();
        q.push(x); sum += x;
    }
    cout << -sum;
    return 0;
}

⭐荷马史诗

和合并果子一样, 都是k叉最小权值哈夫曼树

int main() {
    long long n, k, a, ans = 0; cin >> n >> k; priority_queue<pair<long long, int>> q;
    for (int i = 1; i <= n; ++i) cin >> a, q.push({-a, 0});
    for (int i = (k - 1 - (n - 1) % (k - 1)) % (k - 1); i; --i) q.push({0, 0});
    for (long long dep = 0, t = 0; q.size() > 1; dep = t = 0) {
        for (int i = 1; i <= k; ++i) {
            auto p = q.top(); q.pop();
            t += p.first; dep = min(dep, (long long)p.second);
        }
        q.push({t, dep - 1}); ans += t;
    }
    return cout << -ans << '\n' << -q.top().second << '\n', 0;
}

总结与练习

括号画家

int main() {
    int ans = 0; string s; cin >> s;
    stack<int> st;
    for (int i = 0; i < s.size(); ++i) {
        if (!st.empty() && s[i] == ')' && s[st.top()] == ')') st.pop();
        else if (!st.empty() && s[i] == ']' && s[st.top()] == ']') st.pop();
        else if (!st.empty() && s[i] == '}' && s[st.top()] == '}') st.pop();
        else st.push(i);
        ans = max(ans, i - st.top());
    }
    cout << ans;
    return 0;
}

⭐表达式计算

stack<int> nums;
stack<char> ops;

void cal() {
    int a = nums.top(); nums.pop();
    int b = nums.top(); nums.pop();
    char c = ops.top(); ops.pop();
    switch (c) {
    case '+' : nums.push(b + a); break;
    case '-' : nums.push(b - a); break;
    case '*' : nums.push(b * a); break;
    case '/' : nums.push(b / a); break;
    default : nums.push(pow(b, a));
    }
}

void read(int &i, string& s) {
    int t = 0;
    for (; isdigit(s[i]); ++i) t = t * 10 + s[i] - '0';
    nums.push(t); --i;
}

int main() {
    string s; cin >> s;
    if (s[0] == '-') s = '0' + s; s = '(' + s + ')';
    for (int i = 0; i < s.size(); ++i)
        if (isdigit(s[i])) read(i, s);
        else
            switch (s[i]) {
            case '(' : ops.push('('); break;
            case '+' : case '-' : {
                if (s[i] == '-' && s[i - 1] == '(') {
                    if (s[i + 1] == '(') nums.push(-1), ops.push('*');
                    else read(++i, s), nums.top() = -nums.top();
                } else {
                    while (ops.top() != '(') cal();
                    ops.push(s[i]);
                }
            } break;
            case '*' : case '/' :
                while (ops.top() == '*' || ops.top() == '/' || ops.top() == '^') cal();
                ops.push(s[i]); break;
            case '^' : while (ops.top() == '^') cal(); ops.push(s[i]); break; 
            case ')' : while (!ops.empty() && ops.top() != '(') cal();
                if (!ops.empty()) ops.pop(); break;
            default : cout << "invalid operator!\n";
            }
    cout << nums.top() << endl;
    return 0;
}

⭐城市游戏

int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; ++i) r[0][i] = m + 1;
    for (int i = 1, ml = 0, mr = m + 1; i <= n; ++i, ml = 0, mr = m + 1) {
        for (int j = 1; j <= m; ++j) {
            char c; cin >> c; a[i][j] = c == 'R';
            if (a[i][j]) ml = j, l[i][j] = h[i][j] = 0;
            else l[i][j] = max(l[i - 1][j], ml), h[i][j] = h[i - 1][j] + 1;
        }
        for (int j = m; j; --j)
            if (a[i][j]) mr = j, r[i][j] = m + 1;
            else r[i][j] = min(r[i - 1][j], mr), ans = max(ans, h[i][j] * (r[i][j] - l[i][j] - 1));
    }
    cout << ans * 3;
    return 0;
}

双栈排序

int a[1005], f[1005];
bool v[1005];

int main() {
    string s; int n, k = 1; cin >> n; stack<int> x, y; x.push(1e9); y.push(1e9);
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = n, j = a[i] - 1; i; v[a[i]] = 1, j = a[--i] - 1) {
        if (!v[j]) f[j + 1] = 0;
        else {
            bool g = 0; f[j + 1] = f[j];
            for (int k = i + 1; a[k] != j; ++k) f[j + 1] = max(f[j + 1], max(f[a[k]], a[k]));
        }
    }
    for (int i = 1, c = a[1]; i <= n; c = a[++i]) {
        while (x.top() == k) ++k, s += 'b', x.pop();
        if (x.top() > c && f[c] < y.top()) s += 'a', x.push(c);
        else {
            while (y.top() == k) s += 'd', ++k, y.pop();
            if (y.top() > c) s += 'c', y.push(c);
            else { s = ""; break; }
        }
    }
    if (s.empty()) return cout << 0, 0;
    while (x.top() == k || y.top() == k)
        if (x.top() == k) ++k, s += 'b', x.pop();
        else ++k, s += 'd', y.pop();
    for (auto c : s) cout << c << ' ';
    return 0;
}

滑动窗口

int main() {
    int n, k; cin >> n >> k; vector<int> a, b, c(n); multiset<int> st;
    for (int i = 0; i < k; ++i) cin >> c[i], st.insert(c[i]);
    a.push_back(*st.begin()); b.push_back(*st.rbegin());
    for (int i = k; i < n; ++i) {
        cin >> c[i]; st.erase(st.find(c[i - k])); st.insert(c[i]);
        a.push_back(*st.begin()); b.push_back(*st.rbegin());
    }
    for (auto i : a) cout << i << ' '; cout << '\n';
    for (auto i : b) cout << i << ' ';
    return 0;
}

⭐内存分配

vector<PII> ve; queue<PII> qu;
priority_queue<PII, vector<PII>, greater<PII>> q;

int n, t, m, p, ans, cnt;

bool add(int t, int m, int p) {
    for (auto jt = ve.begin(), it = jt++; jt != ve.end(); ++jt, ++it)
        if (m <= jt->first - it->first - it->second) {
            int start = it->first + it->second;
            return ve.insert(jt, { start, m }), q.push({ t + p, start }), 1;
        }
    return 0;
}

void work(int t) {
    while (!q.empty() && q.top().first <= t) {
        ans = q.top().first;
        while (!q.empty() && q.top().first == ans) {
            auto p = q.top(); q.pop();
            ve.erase(lower_bound(ve.begin(), ve.end(), PII{p.second, 0}));
        }    
        while (!qu.empty() && add(ans, qu.front().first, qu.front().second)) qu.pop();
    }
}

int main() {
    cin >> n; ve.push_back({ -1, 1 }), ve.push_back({ n, 1 });
    while (cin >> t >> m >> p, t || m || p) {
        work(t);
        if (!add(t, m, p)) qu.push({ m, p }), ++cnt;
    }
    return work(2e9), cout << ans << '\n' <<cnt, 0;
}

矩阵

void init() {
    cin >> n >> m >> a >> b;
    for (int i = 1; i <= a * b; ++i) pi[i] = pi[i - 1] * p;
    for (int i = 1; i <= n; ++i) {
        cin >> s + 1;
        for (int j = 1; j <= m; ++j) sumh[i][j] = sumh[i][j - 1] * p + s[j];
    }
    for (int j = b; j <= m; ++j) {
        unsigned long long cur = 0;
        for (int i = 1; i < a; ++i) cur = (cur - sumh[i][j - b]) * pi[b] + sumh[i][j];
        for (int i = a; i <= n; ++i) {
            cur = (cur - sumh[i][j - b]) * pi[b] - (sumh[i - a][j] - 
                sumh[i - a][j - b] * pi[b]) * pi[a * b] + sumh[i][j];
            st.insert(cur);
        }
    }
}

int main() {
    init();
    for (cin >> _; _; --_) {
        unsigned long long cur = 0;
        for (int i = 1; i <= a; ++i) {
            cin >> s + 1;
            for (int j = 1; j <= b; ++j) cur = cur * p + s[j];
        }
        cout << ("01"[st.count(cur)]) << '\n';
    }
    return 0;
}

树形地铁系统

string dfs(string &seq, int &u) {
    vector<string> seqs; string res;
    while (seq[u] == '0') seqs.push_back(dfs(seq, ++u));
    sort(seqs.begin(), seqs.end());
    for (auto &s : seqs) res += s;
    return ++u, '0' + res + '1';
}

int main() {
    for (cin >> _; _; --_) {
        string a, b; cin >> a >> b; x = y = 1;
        a = '0' + a + '1'; b = '0' + b + '1';
        if (dfs(a, x) == dfs(b, y)) puts("same");
        else puts("different");
    }
    return 0;
}

项链

int getmin(char s[], int len) {
    int i = 0, j = 1, k = 0;
    while (i < len && j < len && k < len)
        if (s[(i + k) % len] == s[(j + k) % len]) ++k;
        else {
            if (s[(i + k) % len] < s[(j + k) % len]) j += k + 1;
            else i += k + 1;
            if (i == j) ++j; k = 0;
        }
    return min(i, j);
}

int main() {
    cin >> s >> t;
    int len = strlen(s), a = getmin(s, len), b = getmin(t, len);
    for (int i = 0; i < len; ++i)
        if (s[(a + i) % len] != t[(b + i) % len]) { puts("No"); return; }
    puts("Yes");
    for (int i = 0; i < len; ++i) putchar(s[(a + i) % len]);
    return 0;
}

奶牛矩阵

void kmp() {
    f[1] = 0;
    for (int i = 2, j = 0; i <= n; f[i++] = j) {
        while (j && strcmp(s[i], s[j + 1])) j = f[j];
        if (!strcmp(s[i], s[j + 1])) ++j;
    }
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        cin >> s[i];
        for (int j = 1; j <= m; ++j) if (!wid[j])
            for (int k = j; k < m; ++k)
                if (s[i][k] != s[i][k % j]) { wid[j] = 1; break; }
    }
    for (int i = 1; i <= m; ++i) if (!wid[i]) {
        for (int j = 1; j <= n; ++j) s[j][i] = '\000';
        kmp(); cout << i * (n - f[n]); break;
    }
    return 0;
}

匹配统计

int lens, lent, f[N], extend[N], sum[N], n, m, q;
char s[N], t[N];
void kmp(char* t, int lent) { //t从1开始
    int j = 0, k = 2;
    while (j + 2 <= lent && t[j + 1] == t[j + 2]) ++j;
    f[2] = j; f[1] = lent;
    for (int i = 3, p = k + f[k] - 1; i <= lent; ++i, p = k + f[k] - 1)
        if (i + f[i - k + 1] - 1 < p) f[i] = f[i - k + 1];
        else {
            j = max(0, p - i + 1);
            while (j + i <= lent && t[j + 1] == t[i + j]) ++j;
            f[i] = j; k = i;
        }
}
void ex_kmp(char *s, char *t, int lens, int lent) { //s, t下标都是从1开始
    int j = 0, k = 1;
    while (j + 1 <= min(lens, lent) && s[j + 1] == t[j + 1]) ++j;
    extend[1] = j;
    for (int i = 2, p = k + extend[k] - 1; i <= lens; ++i, p = k + extend[k] - 1)
        if (i + f[i - k + 1] - 1 < p) extend[i] = f[i - k + 1];
        else {
            j = max(0, p - i + 1);
            while (j + i <= lens && j + 1 <= lent && t[j + 1] == s[i + j]) ++j;
            extend[i] = j; k = i;
        }
}
int main() {
    cin >> n >> m >> q >> s + 1 >> t + 1;
    lent = strlen(t + 1); lens = strlen(s + 1);
    kmp(t, lent); ex_kmp(s, t, lens, lent);
    for (int i = 1; i <= lens; ++i) ++sum[extend[i]];
    while (q--) {
        int x; cin >> x;
        cout << sum[x] << '\n';
    }
    return 0;
}

电话列表

bool insert(string& s) {
    int p = 0; bool g = 0;
    for (int i = 0; i < s.size(); ++i) {
        int ch = s[i] - '0';
        p = !tr[p][ch] ? tr[p][ch] = ++tot : tr[p][ch];
        if (f[p]) g = 1;
    }
    ++f[p];
    return g;
}

int main() {
    for (cin >> _; _; --_, tot = 0) {
        cin >> n; memset(tr, 0, sizeof tr); memset(f, 0, sizeof f);
        bool res = 1;
        for (int i = 0; i < n; i ++ ) cin >> s[i];
        sort(s, s + n, [](string& a, string& b){return a.size() < b.size();});
        for (int i = 0; i < n; ++i) if (insert(s[i])) { res = 0; break; }
        puts(res ? "YES" : "NO");
    }
    return 0;
}

黑盒子

int main() {
    cin >> n >> m; priority_queue<int> q, qu;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1, j = 1; i <= m; ++i) {
        for (cin >> b; j <= b;) {
            qu.push(-a[j++]);
            if (!q.empty() && q.top() > -qu.top()) {
                qu.push(-q.top()); q.pop();
                q.push(-qu.top()); qu.pop();
            }
        }
        cout << -qu.top() << '\n'; q.push(-qu.top()); qu.pop();
    }
    return 0;
}

⭐生日礼物

选取k个不相邻的连续线段使和最小(元素大于等于0), 可以用双向链表加小根堆处理
现将所有元素插入小根堆中
ans+=堆顶,删除堆顶,插入堆顶元素左右两个元素的和减去堆顶元素,删除左右两个元素

本题可转化为类似的问题
将连续的正数和负数合并,因为连续的正数和负数一定一起选,0删去
先将所有正数都选上,和记为res, 有cnt个正数
如果正数段数小于M直接输出
要想减少段数有两种方式:
1.删除一段正数
2.将多段正数和连接他们的负数一起选上
第一种选法直接将res-=这个值
第二种则相当于减去其中负数段的绝对值(画图考虑);
进而将连续的负数记为绝对值
进而变成了选(减去)cnt-k连续段和最小, res减去这个值

void remove(int x) { l[r[x]] = l[x]; r[l[x]] = r[x]; vis[x] = 1; }

int main() {
    cin >> n >> m >> a[++tot]; priority_queue<pair<int, int>> qu;
    for (int i = 2; i <= n; ++i) {
        cin >> a[i]; if(!a[i])continue;
        if (a[i] * a[tot] > 0) a[tot] += a[i];
        else {
            if (a[tot] > 0) ans += a[tot], qu.push({-a[tot], tot}), ++cur;
            else qu.push({a[tot], tot});
            a[++tot] = a[i];
        }
    }
    if (a[tot] > 0) ans += a[tot], qu.push({-a[tot], tot}), ++cur;
    else qu.push({a[tot], tot});
    if (cur > m) {
        for (int i = 1; i <= tot; ++i) l[i] = i - 1, r[i] = i + 1;
        while (cur - m) {
            auto p = qu.top(); qu.pop();
            if (vis[p.second]) continue;
            if(a[p.second] < 0 && (l[p.second] == 0 || r[p.second] == tot + 1)) continue;
            ans += p.first; --cur; vis[l[p.second]] = vis[r[p.second]] = 1;
            a[p.second] += a[l[p.second]] + a[r[p.second]];
            qu.push({-abs(a[p.second]), p.second}); remove(r[p.second]), remove(l[p.second]);   
        }
    } 
    return cout << ans, 0;
}

搜索

树与图的遍历

可达性统计

vector<int> h[30001];
bitset<30001> a[30001];

int main() {
    cin >> n >> m; queue<int> q;
    for (int i = 0; i < m; ++i, ++deg[x]) cin >> x >> y, a[x][y] = 1, h[y].push_back(x);
    for (int i = 1; i <= n; ++i) if (!deg[i]) q.push(i);
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (auto y : h[x]) {
            a[y] |= a[x], --deg[y];
            if (!deg[y]) q.push(y);
        }
    }
    for (int i = 1; i <= n; ++i) cout << a[i].count() + 1 << '\n';
    return 0;
}

深度优先搜索

小猫爬山

void dfs(int now, int cnt) {
    if (cnt >= ans) return;
    if (now == n + 1) { ans = min(ans, cnt); return; }
    for (int i = 1; i <= cnt; ++i) if (car[i] + a[now] <= w)
        car[i] += a[now], dfs(now + 1, cnt), car[i] -= a[now];
    car[cnt + 1] = a[now]; dfs(now + 1, cnt + 1); car[cnt + 1] = 0;
}

int main() {
    cin >> n >> w; ans = n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    dfs(1, 0); cout << ans;
    return 0;
}

数独

struct DLX {
    static const int N = 1e5 + 5;
#define IT(i, A, x) for(int i=A[x];i^x;i=A[i])
    int n, m, tot, first[N], siz[N], stk[N], ans;
    int L[N], R[N], U[N], D[N], col[N], row[N];
    void build(const int &r, const int &c) {
        for (int i = 0; i <= c; ++i) L[i] = i - 1, R[i] = i + 1, U[i] = D[i] = i;
        n = r, m = c; L[0] = c, R[c] = 0, tot = c;
        memset(first, 0, sizeof(first)); memset(siz, 0, sizeof(siz));
    }
    void insert(const int &r, const int &c) {
        col[++tot] = c, row[tot] = r, ++siz[c];
        D[tot] = D[c], U[D[c]] = tot, U[tot] = c, D[c] = tot;
        if (!first[r]) first[r] = L[tot] = R[tot] = tot;
        else {
            R[tot] = R[first[r]], L[R[first[r]]] = tot;
            L[tot] = first[r], R[first[r]] = tot;
        }
    }
    void remove(const int &c) {
        L[R[c]] = L[c], R[L[c]] = R[c];
        IT(i, D, c) IT(j, R, i) U[D[j]] = U[j], D[U[j]] = D[j], --siz[col[j]];
    }
    void recover(const int &c) {
        IT(i, U, c) IT(j, L, i) U[D[j]] = D[U[j]] = j, ++siz[col[j]];
        L[R[c]] = R[L[c]] = c;
    }
    bool dance(int dep) {
        if (!R[0]) return ans = dep, 1;
        int c = R[0];
        IT(i, R, 0) if (siz[i] < siz[c]) c = i;
        remove(c);
        IT(i, D, c) {
            stk[dep] = row[i];
            IT(j, R, i) remove(col[j]);
            if (dance(dep + 1)) return 1;
            IT(j, L, i) recover(col[j]);
        }
        recover(c);
        return 0;
    }
#undef IT
} dlx;

int a[10][10];

void insert(int r, int c, int n) {
    int g = (r - 1) / 3 * 3 + (c - 1) / 3 + 1;
    int id = (r - 1) * 81 + (c - 1) * 9 + n;
    dlx.insert(id, (r - 1) * 9 + n);
    dlx.insert(id, 81 + (c - 1) * 9 + n);
    dlx.insert(id, 162 + (g - 1) * 9 + n);
    dlx.insert(id, 243 + (r - 1) * 9 + c);
}

int main() {
    string s;
    while (cin >> s, s != "end") {
        dlx.build(729, 324);
        for (int i = 1; i <= 9; ++i) for (int j = 1; j <= 9; ++j) {
            a[i][j] = s[(i - 1) * 9 + j - 1] == '.' ? 0 : s[(i - 1) * 9 + j - 1] ^ '0';
            for (int v = 1; v <= 9; ++v) if (!a[i][j] || a[i][j] == v) insert(i, j, v);
        }
        dlx.dance(1);
        for (int i = 1; i < dlx.ans; ++i)
            a[(dlx.stk[i] - 1) / 81 + 1][(dlx.stk[i] - 1) / 9 % 9 + 1] = (dlx.stk[i] - 1) % 9 + 1;
        for (int i = 1; i <= 9; ++i) for (int j = 1; j <= 9; ++j) cout << a[i][j]; cout << '\n';
    }
  return 0;
}

剪枝

木棒

bool dfs(int len, int tot, int cur) {
    if (tot == n) return 1;
    if (cur == 0) cur = len;
    for (int i = cur; i; --i)  if (a[i]) {
        --a[i]; if (dfs(len, tot + 1, cur - i)) return 1; ++a[i];
        if (cur == len || cur - i == 0) return 0;
    }
    return 0;
}

int main() {
    while (cin >> n, n) {
        int s = 0, t; memset(a, 0, sizeof(a));
        for (int i = 0; i < n; ++i) { cin >> t; if (t <= 50) ++a[t], s += t; }
        for (int i = 1; i <= s >> 1 && !a[0]; ++i) if (!(s % i) && dfs(i, 0, i)) a[0] = i;
        cout << (a[0] ? a[0] : s) << '\n';
    }
    return 0;
}

⭐生日蛋糕

void dfs(int dep) {
    if (!dep) { if (v == n) ans = min(ans, s); return; }
    for (r[dep] = min((int)sqrt(n - v), r[dep + 1] - 1); r[dep] >= dep; --r[dep])
        for(h[dep] = min((int)((double)(n - v) / r[dep] / r[dep]), h[dep + 1] - 1); h[dep] >= dep; --h[dep]) {
            if (v + minv[dep] > n || s + mins[dep] > ans) continue;
            if (2 * (n - v) / r[dep] + s > ans) continue;
            if (dep == m)  s += r[dep] * r[dep];
            s += 2 * r[dep] * h[dep];
            v += r[dep] * r[dep] * h[dep];
            dfs(dep - 1);
            if (dep == m)  s -= r[dep] * r[dep];
            s -= 2 * r[dep] * h[dep];
            v -= r[dep] * r[dep] * h[dep];
        }
}

int main() {
    cin >> n >> m;
    for(int i = 1; i <= m; ++i) minv[i] = minv[i - 1] + i * i * i, mins[i] = mins[i - 1] + i * i;
    h[m + 1] = r[m + 1] = inf; dfs(m); cout << (ans == inf ? 0 : ans); 
    return 0;
}

数独2

struct DLX {
    static const int N = 2e6 + 5;
#define IT(i, A, x) for(int i=A[x];i^x;i=A[i])
    int n, m, tot, first[N], siz[N], stk[N], ans;
    int L[N], R[N], U[N], D[N], col[N], row[N];
    void build(const int& r, const int& c) {
        for (int i = 0; i <= c; ++i) L[i] = i - 1, R[i] = i + 1, U[i] = D[i] = i;
        n = r, m = c; L[0] = c, R[c] = 0, tot = c;
        memset(first, 0, sizeof(first)); memset(siz, 0, sizeof(siz));
    }
    void insert(const int& r, const int& c) {
        col[++tot] = c, row[tot] = r, ++siz[c];
        D[tot] = D[c], U[D[c]] = tot, U[tot] = c, D[c] = tot;
        if (!first[r]) first[r] = L[tot] = R[tot] = tot;
        else {
            R[tot] = R[first[r]], L[R[first[r]]] = tot;
            L[tot] = first[r], R[first[r]] = tot;
        }
    }
    void remove(const int& c) {
        L[R[c]] = L[c], R[L[c]] = R[c];
        IT(i, D, c) IT(j, R, i) U[D[j]] = U[j], D[U[j]] = D[j], --siz[col[j]];
    }
    void recover(const int& c) {
        IT(i, U, c) IT(j, L, i) U[D[j]] = D[U[j]] = j, ++siz[col[j]];
        L[R[c]] = R[L[c]] = c;
    }
    bool dance(int dep) {
        if (!R[0]) return ans = dep, 1;
        int c = R[0];
        IT(i, R, 0) if (siz[i] < siz[c]) c = i;
        remove(c);
        IT(i, D, c) {
            stk[dep] = row[i];
            IT(j, R, i) remove(col[j]);
            if (dance(dep + 1)) return 1;
            IT(j, L, i) recover(col[j]);
        }
        recover(c);
        return 0;
    }
#undef IT
} dlx;

int a[17][20];
char c[17][20];

void insert(int r, int c, int n) {
    int g = (r - 1) / 4 * 4 + (c - 1) / 4 + 1;
    int id = (r - 1) * 16 * 16 + (c - 1) * 16 + n;
    dlx.insert(id, (r - 1) * 16 + n);
    dlx.insert(id, 16 * 16 + (c - 1) * 16 + n);
    dlx.insert(id, 16 * 16 * 2 + (g - 1) * 16 + n);
    dlx.insert(id, 16 * 16 * 3 + (r - 1) * 16 + c);
}

int main() {
    while (scanf("%s", c[1] + 1) != EOF) {
        for (int i = 2; i <= 16; ++i) scanf("%s", c[i] + 1);
        dlx.build(16 * 16 * 16, 16 * 16 * 4);
        for (int i = 1; i <= 16; ++i) for (int j = 1; j <= 16; ++j) {
            a[i][j] = c[i][j] == '-' ? 0 : c[i][j] - 'A' + 1;
            for (int v = 1; v <= 16; ++v) if (!a[i][j] || a[i][j] == v) insert(i, j, v);
        }
        dlx.dance(1);
        for (int i = 1; i < dlx.ans; ++i)
            c[(dlx.stk[i] - 1) / 16 / 16 + 1][(dlx.stk[i] - 1) / 16 % 16 + 1] = (dlx.stk[i] - 1) % 16 + 'A';
        for (int i = 1; i <= 16; ++i) printf("%s\n", c[i] + 1); puts("");
    }
    return 0;
}

迭代加深

加成序列

bool dfs(int u, int k) {
    if (u == k + 1) return a[u - 1] == n;
    bool v[101] = {0};
    for (int i = u - 1; i; --i)
        for (int j = i; j; --j) {
            int s = a[i] + a[j];
            if (s > n || s <= a[u - 1] || v[s]) continue;
            v[s] = 1; a[u] = s;
            if (dfs(u + 1, k)) return true;
        }
    return false;
}

int main() {
    while (cin >> n, n) {
        int k = 1;
        while (!dfs(2, k)) ++k;
        for (int i = 1; i <= k; ++i) cout << a[i] << ' '; cout << '\n';
    }
    return 0;
}

⭐送礼物

要先排序a, 再双向搜

void dfs(int k, int *a, bool f, int c) {
    if (k == n + f >> 1) {
        if (f) { v.push_back(c); return; }
        auto it = upper_bound(v.begin(), v.end(), m - c);
        if (it == v.begin()) ans = max(ans, c);
        else ans = max(ans, c + *(--it));
        return;
    }
    dfs(k + 1, a, f, c);
    if (0ll + c + a[k] <= m) dfs(k + 1, a, f, c + a[k]);
}

int main() {
    cin >> m >> n; for (int i = 0; i < n; ++i) cin >> a[i]; sort(a, a + n);
    dfs(0, a + (n >> 1), 1, 0); sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end()); dfs(0, a, 0, 0);
    cout << ans;
    return 0;
}

广度优先搜索

立体推箱子

struct node { int x, y, a, b, cnt;} s, der, cur;

int mp[505][505], vis[505][505], tot, n, m, flag;

inline int read(int i, int j) {
    char c = getchar();
    while (c != '#' && c != '.' && c != 'E' && c != 'X' && c != 'O') c = getchar();
    switch (c) {
    case '#' : return -1;
    case '.' : return 1;
    case 'E' : return 0;
    case 'X' : if (!s.x) s.x = i, s.y = j; else s.a = i, s.b = j; return 11;
    case 'O' : if (!der.x) der.x = i, der.y = j; else der.a = i, der.b = j; return 21;
    }
}

inline bool check(const node& t) {
    if (t.x <1 || t.x > n || t.y < 1 || t.y > m || mp[t.x][t.y] == -1) return 0;
    if (t.a == -1)
        if (mp[t.x][t.y] == 0 || vis[t.x][t.y] & 1) return 0;
        else return vis[t.x][t.y] |= 1;
    if (t.a <1 || t.a > n || t.b < 1 || t.b > m || mp[t.a][t.b] == -1) return 0;
    if (t.a == t.x)
        if (vis[t.x][t.y] & (1 << 1)) return 0;
        else return vis[t.x][t.y] |= (1 << 1), 1;
    else
        if (vis[t.x][t.y] & (1 << 2)) return 0;
        else return vis[t.x][t.y] |= (1 << 2), 1;
}

inline bool checked(node& t, queue<node>& qu) {
    if (!check(t)) return 0; qu.push(t);
    if (t.a != der.a || t.x != der.x || t.y != der.y) return 0;
    printf("%d\n", cur.cnt); return flag = 1;
}

int main() {
    while (scanf("%d%d", &n, &m), n && m) {
        s.a = der.a = -1, s.x = der.x = flag = s.cnt = 0;
        memset(vis, 0, sizeof vis);
        for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) mp[i][j] = read(i, j);
        vis[s.x][s.y] |= s.a == -1 ? 1 : s.a == s.x ? 1 << 1 : 1 << 2;
        queue<node> qu; qu.push(s);
        while (!qu.empty()) {
            s = qu.front(); qu.pop(); cur.cnt = s.cnt + 1;
            if (s.a == -1) {
                cur.x = s.x - 2, cur.a = s.x - 1, cur.b = cur.y = s.y;
                if (checked(cur, qu)) break;
                cur.x = cur.a = s.x, cur.y = s.y - 2, cur.b = s.y - 1;
                if (checked(cur, qu)) break;
                cur.x = cur.a = s.x, cur.y = s.y + 1, cur.b = s.y + 2;
                if (checked(cur, qu)) break;
                cur.x = s.x + 1, cur.a = s.x + 2, cur.y = cur.b = s.y;
                if (checked(cur, qu)) break;
            } else
                if (s.a == s.x) {
                    cur.x = cur.a = s.a - 1, cur.b = s.b, cur.y = s.y;
                    if (checked(cur, qu)) break;
                    cur.x = cur.a = s.a + 1;
                    if (checked(cur, qu)) break;
                    cur.a = -1, cur.x = s.x, cur.y = s.y - 1;
                    if (checked(cur, qu)) break;
                    cur.y = s.y + 2;
                    if (checked(cur, qu)) break;
                } else {
                    cur.a = -1, cur.x = s.x - 1, cur.y = s.y;
                    if (checked(cur, qu)) break;
                    cur.x = s.x + 2;
                    if (checked(cur, qu)) break;
                    cur.x = s.x, cur.a = s.a, cur.y = cur.b = s.y - 1;
                    if (checked(cur, qu)) break;
                    cur.y = cur.b = s.y + 1;
                    if (checked(cur, qu)) break;
                }
        }
        if (!flag) puts("Impossible");
    }
    return 0;
}

矩阵距离

queue<pair<pair<int, int>, int> > qu;

inline int read(int i, int j) {
    char c = getchar();
    while (c != '0' && c != '1') c = getchar();
    return c == '1' ? qu.push({{i, j}, 0}), 0; -1;
}

void check(int x, int y, int cnt) {
    if (x < 1 || x > n || y < 1 || y > m)return;
    if (mp[x][y] == -1) mp[x][y] = cnt + 1, qu.push({{x, y}, mp[x][y]});
}

int main() {
    cin >> n >> m; for(int i = 1; i <= n; ++ i) for(int j = 1; j <= m; ++ j) mp[i][j] = read(i, j);
    while(!qu.empty()) {
        auto p = qu.front();qu.pop();
        for (int i = 0; i < 4; ++ i) check(p.first.first + d[i][0], p.first.second + d[i][1], p.second);
    }
    for(int i = 1; i <= n; ++ i, puts("")) for(int j = 1; j <= m; ++ j) printf("%d ", mp[i][j]);
    return 0;
}

⭐推箱子

int ma[21][21], n, m, _, d[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
char der[5] = "nswe";

int read() {
    char c = getchar();
    while (c != '#' && c != '.' && c != 'T' && c != 'B' && c != 'S') c = getchar();
    if (c == '#') return 1;
    if (c == '.') return 0;
    if (c == 'T') return 2;
    if (c == 'B') return 3;
    return 4;
}

queue<pair<PII, string>> q;
set<PII> st;

string bfs_man(PII a, PII b, PII box) {
    queue<pair<PII, string>>().swap(q); set<PII>().swap(st);
    q.push({ a, "" }); st.insert(a);
    if (ma[b.fi][b.se] == 1 || ma[a.fi][a.se] == 1) return "1";
    while (!q.empty()) {
        string s = q.front().se;
        PII cur = q.front().fi; q.pop();
        if (cur == b) return s;
        rep(i, 0, 3) {
            int x = cur.fi + d[i][0], y = cur.se + d[i][1];
            if (x > n || x < 1 || y > m || y < 1) continue;
            if (ma[x][y] == 1 || (x == box.fi && y == box.se)) continue;
            if (!st.count({ x, y })) 
                q.push({ {x, y}, s + der[i] }), st.insert({ x, y });
        }
    }
    return "1";
}

queue<pair<pair<PII, PII>, string>> qu;
set <pair<PII, PII>> stt;

string bfs_box(PII b, PII r, PII t) {
    set<pair<PII, PII>>().swap(stt); qu.push({ {b, r}, "" });
    vector<string> ve;
    while (!qu.empty()) {
        b = qu.front().fi.fi, r = qu.front().fi.se;
        string s = qu.front().se; qu.pop();
        if (b == t) {ve.emplace_back(s); continue;}
        if (!ve.empty()) continue;
        for (int i = 0; i <= 3; ++i) {
            int x = b.fi + d[i][0], y = b.se + d[i][1];
            int rx = b.fi - d[i][0], ry = b.se - d[i][1];
            if (x > n || x < 1 || y > m || y < 1 || ma[x][y] == 1) continue;
            string pe = bfs_man(r, { rx, ry }, b);
            if (pe == "1") continue;
            string cur = s + pe + char(der[i] ^ 32);
            if (!stt.count({ {x, y}, b }))
                qu.push({ {{x, y}, b}, cur }), stt.insert({ {x, y}, b });
        }
    }
    if (ve.empty()) return "";
    return *min_element(ve.begin(), ve.end(), [](string& a, string& b) {
        if (a.size() != b.size()) return a.size() < b.size();
        for (int i = 0; i < a.size(); ++i) if (a[i] != b[i]) for (int j = 0; j < 4; ++j)
            if (a[i] == der[j] || a[i] == char(der[j] ^ 32)) return true;
            else if (b[i] == der[j] || b[i] == char(der[j] ^ 32)) return false;
    });
}

int main() {
    int bx, by, tx, ty, sx, sy;
    while (scanf("%d%d", &n, &m), n + m) {
        printf("Maze #%d\n", ++_);
        rep(i, 1, n) rep(j, 1, m) switch (ma[i][j] = read()) {
        case 2 : tx = i, ty = j; break;
        case 3 : bx = i, by = j; break;
        case 4 : sx = i, sy = j; break;
        }
        string ans = bfs_box({ bx, by }, { sx, sy }, { tx, ty });
        if (ans == "") puts("Impossible.\n");
        else printf("%s\n\n", ans.c_str());
    }
    return 0;
}

广搜变形

电路维修

int h[N], ne[N << 2], co[N << 2], to[N << 2], tot;

void add(int u, int v, int c) {
    ne[++tot] = h[u]; h[u] = tot; to[tot] = v; co[tot] = c;
}

bool read() {
    char c = getchar();
    while (c != '/' && c != '\\') c = getchar();
    return c == '/';
}

deque<PII> q;

void dij() {
    q.push_back({ 0, 1 });
    rep(i, 2, s) d[i] = N << 1;
    while (!q.empty()) {
        int x = q.front().second; q.pop_front();
        for (int i = h[x]; i; i = ne[i])
            if (co[i] && d[to[i]] > d[x] + 1) {
                d[to[i]] = d[x] + 1;
                q.push_back({ d[x] + 1, to[i] });
            } else if (!co[i] && d[to[i]] > d[x]) {
                d[to[i]] = d[x];
                q.push_front({ d[x], to[i] });
            }
    }
}

int main() {
    for (cin >> _; _; --_) {
        cin >> n >> m; tot = 0;
        s = m * n + n + m + 1;
        rep(i, 1, s) h[i] = 0;
        rep(i, 1, n) rep(j, 1, m) {
            int a = i * m + i - m + j - 1, b = a + 1;
            int c = a + m + 1, d = c + 1, k = read();
            add(a, d, k); add(d, a, k);
            add(b, c, !k); add(c, b, !k);
        }
        dij();
        if (d[s] >= (N << 1)) cout << "NO SOLUTION\n";
        else cout << d[s] << '\n';
    }
    return 0;
}

⭐装满油的油箱

int bfs(int s, int t, int c) {
    vector<vector<int>> v(n + 1, vector<int>(c + 1, 2e9)); v[s][0] = 0;
    priority_queue<pair<int, pair<int, int>>> q; q.push({0, {0, s}});
    while (!q.empty()) {
        auto x = q.top(); q.pop();
        if (x.se.se == t) return -x.fi;
        if (v[x.se.se][x.se.fi] ^ -x.fi) continue;
        if (x.se.fi < c && umin(v[x.se.se][x.se.fi + 1], -x.fi + a[x.se.se]))
            q.push({x.fi - a[x.se.se], {x.se.fi + 1, x.se.se}});
        for (auto y : h[x.se.se]) if (x.se.fi >= y.se && umin(v[y.fi][x.se.fi - y.se], -x.fi))
            q.push({x.fi, {x.se.fi - y.se, y.fi}});
    }
    return -1;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i) cin >> a[i];
    for (int i = 0; i < m; ++i) {
        int u, v, c; cin >> u >> v >> c;
        h[u].push_back({v, c}); h[v].push_back({u, c});
    }
    for (cin >> m; m; --m) {
        int s, t, c, ans; cin >> c >> s >> t;
        ans = bfs(s, t, c);
        if (ans > -1) cout << ans << '\n';
        else cout << "impossible\n";
    }
    return 0;
}

噩梦

bool check(int x, int y, int t, PII gh[]) {
    if (x < 1 || x > n || y < 1 || y > m || g[x][y] == 'X') return 0;
    if (abs(x - gh[0].first) + abs(y - gh[0].second) <= t * 2) return 0;
    if (abs(x - gh[1].first) + abs(y - gh[1].second) <= t * 2) return 0;
    return 1;
}

int bfs() {
    memset(v, 0, sizeof v); int cnt = 0; PII boy, girl, gh[2];
    for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j)
        if (g[i][j] == 'M') boy = { i, j };
        else if (g[i][j] == 'G') girl = { i, j };
        else if (g[i][j] == 'Z') gh[cnt++] = { i, j };
    queue<PII> qb, qg; qb.push(boy); qg.push(girl);
    for (int t = 1; !qb.empty() || !qg.empty(); ++t) {
        for (int i = 1; i <= 3; ++i) for (int j = 1, len = qb.size(); j <= len; ++j) {
            auto cur = qb.front(); qb.pop();
            if (!check(cur.fi, cur.se, t, gh)) continue;
            for (int k = 0; k < 4; ++k) {
                int x = cur.fi + d[k][0], y = cur.se + d[k][1];
                if (check(x, y, t, gh))
                    if (v[x][y] == 2) return t;
                    else if (!v[x][y]) v[x][y] = 1, qb.push({ x, y });
            }
        }
        for (int j = 1, len = qg.size(); j <= len; ++j) {
            auto cur = qg.front(); qg.pop();
            if (!check(cur.fi, cur.se, t, gh)) continue;
            for (int k = 0; k < 4; ++k) {
                int x = cur.fi + d[k][0], y = cur.se + d[k][1];
                if (check(x, y, t, gh))
                    if (v[x][y] == 1) return t;
                    else if (!v[x][y]) v[x][y] = 2, qg.push({ x, y });
            }
        }
    }
    return -1;
}

int main() {
    for (cin >> _; _; --_) {
        cin >> n >> m;
        for (int i = 1; i <= n; ++i) cin >> g[i] + 1;
        cout << bfs() << '\n';
    }
    return 0;
}

A*

⭐第k短路

vector<pair<int, int>> h[1001], g[1001];
int d[3][1001];

void dij(int s, int *d, vector<pair<int, int>>* h) {
    for (int i = 0; i <= n; ++i) d[i] = 0x3f3f3f3f; d[s] = 0;
    priority_queue<pair<int, int>> q; q.push({0, s});
    while (!q.empty()) {
        auto x = q.top(); q.pop();
        if (-x.fi ^ d[x.se]) continue;
        for (auto y : h[x.se]) if (umin(d[y.fi], -x.fi + y.se)) q.push({-d[y.fi], y.fi});
    }
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        int u, v, c; cin >> u >> v >> c;
        h[u].push_back({v, c}); g[v].push_back({u, c});
    }
    cin >> s >> t >> k; if (s == t) ++k;
    dij(t, d[0], g); dij(t, d[1], h); dij(s, d[2], h);
    for (int i = 1; i <= n; ++i) if (i != t && max(d[0][i], max(d[1][i], d[2][i])) < d[0][0]) m = -1;
    if (m != -1) return cout << -1, 0;
    priority_queue<pair<int, pair<int, int>>> q; q.push({-d[0][s], {0, s}});
    while (k && !q.empty()) {
        auto c = q.top(); q.pop(); m = -c.fi; auto x = c.se;
        if (x.se == t) --k;
        for (auto y : h[x.se]) q.push({-d[0][y.fi] - x.fi - y.se, {x.fi + y.se, y.fi}});
    }
    cout << (k ? -1 : m);
    return 0;
}

八数码

int de[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
char der[5] = "urdl";

int f(string& s) {
    int res = 0;
    for (int i = 0; i < 9; ++i) if (s[i] != 'x') {
        int t = s[i] - '1';
        res += abs(i / 3 - t / 3) + abs(i % 3 - t % 3);
    }
    return res;
}

string bfs(string s) {
    priority_queue<pair<int, string>> q; q.push({ f(s), s });
    unordered_map<string, int> d; d[s] = 0;
    unordered_map<string, bool> v;
    unordered_map<string, pair<string, char>> pr;
    string res, t = "12345678x";
    while (!q.empty()) {
        auto x = q.top(); q.pop();
        if (x.se == t) break;
        if (v[x.se]) continue; v[x.se] = true;
        int a, b;
        for (int i = 0; i < 9; ++i) if (x.se[i] == 'x') a = i / 3, b = i % 3;
        for (int i = 0; i < 4; ++i) {
            int z = a + de[i][0], w = b + de[i][1];
            if (z < 0 || z >= 3 || w < 0 || w >= 3) continue;
            auto cur = x.se; swap(cur[a * 3 + b], cur[z * 3 + w]);
            if (!d.count(cur) || d[cur] > d[x.se] + 1) {
                d[cur] = d[x.se] + 1;
                pr[cur] = { x.se, der[i] };
                q.push({ -f(cur) - d[cur], cur});
            }
        }
    }
    while (s != t) res += pr[t].se, t = pr[t].fi;
    reverse(res.begin(), res.end());
    return res;
}

int main() {
    string g, s; char c;
    while (cin >> c) { g += c; if (c != 'x') s += c; }
    int t = 0;
    for (int i = 0; i < 8; ++i) for (int j = i + 1; j < 8; ++j) t += s[i] > s[j];
    if (t & 1) puts("unsolvable");
    else cout << bfs(g) << '\n';
    return 0;
}

IDA*

⭐排书

通过考虑后继想估价函数

int f() {
    int res = 0;
    rep (i, 1, n - 1) if (a[i + 1] != a[i] + 1) ++res;
    return (res + 2) / 3;
}

bool dfs(int u) {
    int ff = f();
    if (u + ff > ans) return false;
    if (ff == 0) return true;
    rep (l, 1, n) rep (r, l, n) rep (k, r + 1, n) {
        memcpy(w[u], a, sizeof a);
        int idx = l - 1;
        rep (i, r + 1, k) a[++idx] = w[u][i];
        rep (i, l, r) a[++idx] = w[u][i];
        if (dfs(u + 1)) return true;
        memcpy(a, w[u], sizeof a);
    }
    return false;
}

int main() {
    for (cin >> _; _; --_) {
        cin >> n; ans = 0;
        rep (i, 1, n) cin >> a[i];
        while (ans < 5 && !dfs(0)) ++ans;
        if (ans >= 5) cout << "5 or more\n";
        else cout << ans << '\n';
    }
    return 0;
}

⭐回转游戏

int d[8][7] = {
    {1, 3, 7, 12, 16, 21, 23}, {2, 4, 9, 13, 18, 22, 24},
    {11, 10, 9, 8, 7, 6, 5}, {20, 19, 18, 17, 16, 15, 14},
    {24, 22, 18, 13, 9, 4, 2}, {23, 21, 16, 12, 7, 3, 1},
    {14, 15, 16, 17, 18, 19, 20}, {5, 6, 7, 8, 9, 10, 11} }, a[25];
int b[8] = {7, 8, 9, 12, 13, 16, 17, 18}, ls[9] = {5, 4, 7, 6, 1, 0, 3, 2, -1};
char s[] = "ABCDEFGH";

int f() {
    int c[3] = { 0 };
    for (int i = 0; i < 8; ++i) ++c[a[b[i]] - 1];
    return 8 - (*max_element(c, c + 3));
}

int dfs(int k, string& op, int las) {
    int cur = f(), ans;
    if (!cur) return a[b[0]];
    if (k < cur) return 0;
    for (int i = 0; i < 8; ++i, op.pop_back()) {
        if (i == ls[las]) { op += 'a'; continue; }
        for (int j = 1; j < 7; ++j) swap(a[d[i][j - 1]], a[d[i][j]]);
        op += s[i]; if (ans = dfs(k - 1, op, i)) return ans;
        for (int j = 6; j; --j) swap(a[d[i][j]], a[d[i][j - 1]]);
    }
    return 0;
}

int main() {
    while (cin >> a[1], a[1]) {
        for (int i = 2; i <= 24; ++i) cin >> a[i];
        int k = 0, ans = 0; string op;
        while (!(ans = dfs(k, op, 8))) ++k;
        cout << (op.empty() ? "No moves needed" : op) << '\n' << ans << '\n';
    }
    return 0;
}

⭐破坏正方形

bool st[61], cur[61];
VI v[56];

void init() {
    rep (k, 1, n) rep (i, 0, n - k) rep (j, 0, n - k) {
        VI().swap(v[++cnt]); int p = i * t + j + 1, q = p + n;
        rep (g, 0, k - 1)
            v[cnt].pb(p + g), v[cnt].pb(p + g + k * t),
            v[cnt].pb(q + g * t), v[cnt].pb(q + g * t + k);
    }
}

bool check(VI &v, bool st[]) {
    for (int x : v) if (st[x]) return false;
    return true;
}

int f() {
    memcpy(cur, st, sizeof st);
    int res = 0;
    rep (i, 1, cnt) if (check(v[i], cur) && ++res)
        for (int x : v[i]) cur[x] = true;
    return res;
}

bool dfs(int dep) {
    if (f() > dep) return false;
    rep (i, 1, cnt) if (check(v[i], st)) {
        for (int x : v[i]) {
            st[x] = true;
            if (dfs(dep - 1)) return true;
            st[x] = false;
        }
        return false;
    }
    return true;
}

int main() {
    for (cin >> _; _; --_) {
        cin >> n >> m; t = (n << 1) + 1;
        memset(st, 0, sizeof st); cnt = 0; init();
        rep (i, 1, m) cin >> ans, st[ans] = 1; ans = 0;
        while (!dfs(ans)) ++ans;
        cout << ans << '\n';
    }
    return 0;
}

总结与练习

靶形数独

struct DLX {
    static const int N = 1e5 + 5;
#define IT(i, A, x) for(int i=A[x];i^x;i=A[i])
    int n, m, tot, first[N], siz[N], stk[N], ans;
    int L[N], R[N], U[N], D[N], col[N], row[N], mx, w[N];
    void build(const int &r, const int &c) {
        for (int i = 0; i <= c; ++i) L[i] = i - 1, R[i] = i + 1, U[i] = D[i] = i;
        n = r, m = c; L[0] = c, R[c] = 0, tot = c;
        memset(first, 0, sizeof(first)); memset(siz, 0, sizeof(siz));
    }
    void insert(const int &r, const int &c, const int &W) {
        col[++tot] = c, row[tot] = r, w[tot] = W, ++siz[c];
        D[tot] = D[c], U[D[c]] = tot, U[tot] = c, D[c] = tot;
        if (!first[r]) first[r] = L[tot] = R[tot] = tot;
        else {
            R[tot] = R[first[r]], L[R[first[r]]] = tot;
            L[tot] = first[r], R[first[r]] = tot;
        }
    }
    void remove(const int &c) {
        L[R[c]] = L[c], R[L[c]] = R[c];
        IT(i, D, c) IT(j, R, i) U[D[j]] = U[j], D[U[j]] = D[j], --siz[col[j]];
    }
    void recover(const int &c) {
        IT(i, U, c) IT(j, L, i) U[D[j]] = D[U[j]] = j, ++siz[col[j]];
        L[R[c]] = R[L[c]] = c;
    }
    bool dance(int dep, int cur) {
        if (!R[0]) return mx = max(cur, mx), 1;
        int c = R[0];
        IT(i, R, 0) if (siz[i] < siz[c]) c = i;
        remove(c);
        IT(i, D, c) {
            stk[dep] = row[i];
            IT(j, R, i) remove(col[j]);
            dance(dep + 1, cur + w[i]);
            IT(j, L, i) recover(col[j]);
        }
        recover(c);
        return 0;
    }
#undef IT
} dlx;

int a[10][10];

void insert(int r, int c, int n, int w) {
    int g = (r - 1) / 3 * 3 + (c - 1) / 3 + 1;
    int id = (r - 1) * 81 + (c - 1) * 9 + n;
    dlx.insert(id, (r - 1) * 9 + n, w);
    dlx.insert(id, 81 + (c - 1) * 9 + n, w);
    dlx.insert(id, 162 + (g - 1) * 9 + n, w);
    dlx.insert(id, 243 + (r - 1) * 9 + c, w);
}

int main() {
    dlx.build(729, 324);
    for (int i = 1; i <= 9; ++i) for (int j = 1; j <= 9; ++j) {
        cin >> a[i][j]; int d = max(abs(i - 5), abs(j - 5));
        for (int v = 1; v <= 9; ++v) if (!a[i][j] || a[i][j] == v) insert(i, j, v, (10 - d) * v);
    }
    dlx.dance(1, 0); cout << (dlx.mx ? dlx.mx : -1);
    return 0;
}

虫食算

int n, q[27], path[27];;
char e[3][27];
bool st[27];

bool check(){
    for (int i = n - 1, t = 0; i >= 0; --i) {
        int a = e[0][i] - 'A', b = e[1][i] - 'A', c = e[2][i] - 'A';
        if (path[a] != -1 && path[b] != -1 && path[c] != -1) {
            a = path[a], b = path[b], c = path[c];
            if (t != -1) {
                if ((a + b + t) % n != c) return 0;
                if (!i && a + b + t >= n) return 0;
                t = (a + b + t) / n;
            } else {
                if ((a + b + 0) % n != c && (a + b + 1) % n != c) return 0;
                if (!i && a + b >= n) return 0;
            }
        }
        else t = -1;
    }
    return 1;
}

bool dfs(int u){
    if (u == n) return true;
    for (int i = 0; i < n; ++i) if (!st[i]) {
        st[i] = 1; path[q[u]] = i;
        if (check() && dfs(u + 1)) return true;
        st[i] = 0; path[q[u]] = -1;
    }
    return false;
}

int main(){
    cin >> n >> e[0] >> e[1] >> e[2];
    for (int i = n - 1, k = 0; i >= 0; --i) for (int j = 0; j < 3; ++j) if (!st[e[j][i] - 'A'])
        st[e[j][i] - 'A'] = 1, q[k++] = e[j][i] - 'A';
    memset(st, 0, sizeof st); memset(path, -1, sizeof path);
    dfs(0);
    for (int i = 0; i < n; ++i) cout << path[i] << ' ';
    return 0;
}

⭐玛雅游戏

多次声明stl, 复制, 尽管开了噢O2, 还是比memcpy慢两倍, 差点t

bool v[7][5];

bool check(vector<vector<int>>& a) {
    for (int j = 0; j < 5; ++j) if (a[0][j]) return 0;
    return 1;
}

void drop(vector<vector<int>>& a) {
    for (int j = 0, k = 0; j < 5; ++j, k = 0) for (int i = 0; i < 7; ++i)
        if (a[i][j]) swap(a[k++][j], a[i][j]);
}

bool isdelet(vector<vector<int>>& b) {
    bool f = 0;
    for (int i = 0; i < 7; ++i) for (int j = 0; j < 5; ++j) if (b[i][j]) {
        if (j < 3 && b[i][j] == b[i][j + 1] && b[i][j + 2] == b[i][j])
            f = v[i][j] = v[i][j + 1] = v[i][j + 2] = 1;
        if (i < 5 && b[i][j] == b[i + 1][j] && b[i + 2][j] == b[i][j])
            f = v[i][j] = v[i + 1][j] = v[i + 2][j] = 1;
    }
    if (!f) return 0;
    for (int i = 0; i < 7; ++i) for (int j = 0; j < 5; ++j) if (v[i][j]) b[i][j] = v[i][j] = 0;
    return f;
}

bool dfs(vector<vector<int>>& b, vector<pair<pair<int, int>, int>>& path, int n) {
    if (check(b)) return 1;
    if (!n) return 0;
    for (int j = 0; j < 5; ++j) for (int i = 0; i < 7; ++i) if (b[i][j]) {
        if (j < 4 && b[i][j + 1] != b[i][j]) {
            vector<vector<int>> a(b);
            swap(a[i][j], a[i][j + 1]); path.push_back({ {j, i}, 1 });
            for (int k = i - 1; ~k && !a[k][j + 1]; --k) swap(a[k][j + 1], a[k + 1][j + 1]);
            for (int k = i; k < 6 && !a[k][j]; ++k) swap(a[k][j], a[k + 1][j]);
            while (isdelet(a)) drop(a);
            if (dfs(a, path, n - 1)) return 1; path.pop_back();
        }
        if (j && !b[i][j - 1]) {
            vector<vector<int>> a(b);
            swap(a[i][j], a[i][j - 1]); path.push_back({ {j, i}, -1 });
            for (int k = i - 1; ~k && !a[k][j - 1]; --k) swap(a[k][j - 1], a[k + 1][j - 1]);
            for (int k = i; k < 6 && !a[k][j]; ++k) swap(a[k][j], a[k + 1][j]);
            while (isdelet(a)) drop(a);
            if (dfs(a, path, n - 1)) return 1; path.pop_back();
        }
    }
    return 0;
}

int main() {
    int n; cin >> n; vector<vector<int>> a(7, vector<int>(5));
    for (int i = 0; i < 5; ++i) for (int j = 0, c; cin >> c, c; ++j) a[j][i] = c;
    vector<pair<pair<int, int>, int>> path;
    dfs(a, path, n);
    if (path.empty()) return cout << -1, 0;
    for (auto& i : path) cout << i.first.first << ' ' << i.first.second << ' ' << i.second << '\n';
    return 0;
}

巴士

vector<pair<int, PII>> li;

bool check(int a, int d) {
    for (int i = a; i < 60; i += d) if (b[i] == 0) return 0;
    return true;
}

bool dfs(int k, int cnt, int tot) {
    if (cnt == ans) return tot == n;
    rep (i, k, li.size() - 1) {
        int& a = li[i].se.fi, d = li[i].se.se; 
        if (-li[i].fi * (ans - cnt) + tot < n) break;
        if (!check(a, d)) continue;
        for (int j = a; j < 60; j += d) --b[j];
        if (dfs(i, cnt + 1, tot - li[i].fi)) return true;
        for (int j = a; j < 60; j += d) ++b[j];
    }
    return false;
}

int main() {
    cin >> n;
    rep (i, 1, n) cin >> m, ++b[m];
    rep (i, 0, 59) rep (d, i + 1, 59 - i)
        if (check(i, d)) li.pb({-(59 - i) / d - 1, {i, d}});
    sort(li.begin(), li.end());
    while (ans < 17 && !dfs(0, 0, 0)) ++ans;
    cout << ans;
    return 0;
}

导弹防御系统

bool dfs(int cx, int cy, int k, int dep) {
    if (cx + cy > dep) return 0;
    if (k == n) return 1;
    int c = cx; x[c] = -1;
    for (int i = 0; i < cx; ++i) if (x[i] > x[c] && x[i] < a[k]) c = i;
    swap(a[k], x[c]);
    if (dfs(cx + (c == cx), cy, k + 1, dep)) return 1;
    swap(a[k], x[c]);
    y[c = cy] = 2e9;
    for (int i = 0; i < cy; ++i) if (y[i] < y[c] && y[i] > a[k]) c = i;
    swap(a[k], y[c]);
    if (dfs(cx, cy + (c == cy), k + 1, dep)) return 1;
    swap(a[k], y[c]);
    return 0;
}

int main() {
    while (cin >> n, n) {
        for (int i = 0; i < n; ++i) cin >> a[i];
        int ans = 1;
        while (!dfs(0, 0, 0, ans))  ++ans;
        cout << ans << '\n';
    }
    return 0;
}

武士风度的牛

bool check(int x,int y) {
    return min(x, y) >= 1 && x <= n && y <= m && s[x][y] != '*' && dis[x][y] == -1;
}

int bfs() {
    memset(dis, -1, sizeof(dis));
    queue<pair<int, int>> q; q.push({st.first,st.second});
    dis[st.first][st.second] = 0;
    while(q.size()) {
        p = q.front(); q.pop();
        for(int i = 0; i < 8; ++i) {
            int x = p.first + dx[i], y = p.second + dy[i];
            if (check(x,y)) {
                dis[x][y] = dis[p.first][p.second] + 1;
                q.push({x, y});
                if (x == ed.first && y == ed.second) return dis[x][y];
            }
        }
    }
    return -1;
}

int main() {
    cin >> m >> n;
    for(int i = 1; i <= n; ++i) {
        cin >> s[i] + 1;
        for(int j = 1; j <= m; ++j)
            if (s[i][j] == 'K') st = {i, j};
            else if (s[i][j]=='H') ed = {i, j};
    }
    cout << bfs();
    return 0;
}

乳草的入侵

int main() {
    cin >> m >> n >> y >> x; x = n - x + 1;
    for (int i = 1; i <= n; ++i) cin >> (st[i] + 1);
    st[x][y] = '*'; queue<pair<int, int>> q; q.push({x, y});
    for (; !q.empty(); ++ans) for (int j = 1, len = q.size(); j <= len; ++j) {
        auto p = q.front(); q.pop();
        for (int i = 0; i < 8; ++i) {
            int x = p.first + d[i][0], y = p.second + d[i][1];
            if (x < 1 || x > n || y < 1 || y > m || st[x][y] == '*') continue;
            st[x][y] = '*'; q.push({ x, y });
        }
    }
    cout << ans - 1;
    return 0;
}

字符串变换

map<string, int> mp;
set<string> st;

int bfs(string s, bool f) {
    if (f && mp.count(s)) return mp[s];
    queue<pair<string, int>> q; q.push({ s, mp[s] = 0 });
    while (!q.empty()) {
        s = q.front().first; c = q.front().second; q.pop();
        for (int i = 1; i <= cnt; ++i)
            for (int p = s.find(ch[i][f], 0); p != string::npos; p = s.find(ch[i][f], p + 1)) {
                string cur = s;
                cur.replace(p, ch[i][f].size(), ch[i][f ^ 1]);
                if (!f) { if (!mp[cur]) { mp[cur] = mp[s] + 1; if (c < 4) q.push({ cur, c + 1 }); } }
                else if (mp[cur]) return mp[cur] + c + 1;
                else if (!st.count(cur)) st.insert(cur), q.push({ cur, c + 1 });
            }
    }
    return -1;
}

int main() {
    cin >> a >> b; mp[a] = 0;
    while (cin >> ch[++cnt][0] >> ch[cnt][1]);
    bfs(a, 0);
    ans = bfs(b, 1);
    if (~ans) cout << ans;
    else cout << "NO ANSWER!";
   

⭐天气预报

struct node{ int day, x, y, s[4]; };
bool a[366][4][4], vis[366][4][4][7][7][7][7];

bool check(int x, int y, int day, int d[]) {
    if (x < 0 || y < 0 || x > 2 || y > 2) return 0;
    rep (i, 0, 1) rep (j, 0, 1) if (a[day][x + i][y + j]) return 0;
    return max(max(d[0], d[1]), max(d[2], d[3])) < 7;
}

bool bfs() {
    if (a[1][1][1] || a[1][1][2] || a[1][2][1] || a[1][2][2]) return 0;
    queue<node> q; q.push({1, 1, 1, {1, 1, 1, 1}});
    memset(vis,0,sizeof(vis));
    int sd[4]; vis[1][1][1][1][1][1][1]=1;
    while (!q.empty()) {
        node cur = q.front(); q.pop();
        if (cur.day == n) return 1;
        rep (i, 0, 8) {
            int x = cur.x + dx[i], y = cur.y + dy[i];
            rep (j, 0, 3) sd[j] = cur.s[j] + 1;
            if (x == 0 && y == 0) sd[0] = 0;
            if (x == 0 && y == 2) sd[1] = 0;
            if (x == 2 && y == 0) sd[2] = 0;
            if (x == 2 && y == 2) sd[3] = 0;
            if (check(x, y, cur.day + 1, sd) &&
                !vis[cur.day + 1][x][y][sd[0]][sd[1]][sd[2]][sd[3]]) {
                q.push({cur.day + 1, x, y, {sd[0], sd[1], sd[2], sd[3]}});
                vis[cur.day + 1][x][y][sd[0]][sd[1]][sd[2]][sd[3]] = 1;
            }
        }
    }
    return 0;
}

int main() {
    while(cin >> n, n) {
        rep (i, 1, n) rep (j, 0, 3) rep (k, 0, 3) cin >> a[i][j][k];
        cout << bfs() << '\n';
    }
    return 0;
}

⭐立体推箱子2

struct node { int x, y, z, t; }t, cur;

int dx[3][4] = {{-2, 0, 1, 0}, {-1, 0, 2, 0}, {-1, 0, 1, 0}};
int dy[3][4] = {{0, 1, 0, -2}, {0, 1, 0, -1}, {0, 2, 0, -1}};
int dz[3][4] = {{1, 2, 1, 2}, {0, 1, 0, 1}, {2, 0, 2, 0}};
int mp[9][9][3], d[3][3][3][4];

void bfs(int x, int y, int z) {
    queue<node> q; q.push({ x, y, z, 0 });
    memset(mp, -1, sizeof mp); mp[x][y][z] = 0;
    while (!q.empty()) {
        t = q.front(); q.pop(); cur.t = t.t + 1;
        rep(i, 0, 3) {
            cur.x = t.x + dx[t.z][i]; cur.y = t.y + dy[t.z][i]; cur.z = dz[t.z][i];
            if (cur.x < 0 || cur.x > 5 || cur.y < 0 || cur.y > 5) continue;
            if (mp[cur.x][cur.y][cur.z] == -1 || mp[cur.x][cur.y][cur.z] > cur.t)
                mp[cur.x][cur.y][cur.z] = cur.t, q.push(cur);
        }
    }
}

void init() {
    rep (k, 0, 2) rep (i, 0, 2) rep (j, 0, 2) {
        bfs(i + 3, j + 3, k);
        d[i][j][k][0] = mp[3][0][0]; d[i][j][k][1] = mp[3][3][0];
        d[i][j][k][2] = mp[0][0][0]; d[i][j][k][3] = mp[0][3][0];
    }
}

ll a, b;
char op;

ll calc(int c) {
    ll x = a / 3, y = b / 3; a %= 3, b %= 3;
    ll ans = d[a][b][c][1] + (x << 1) + (y << 1);
    if (y) ans = min(ans, d[a][b][c][0] + (x << 1) + (y - 1 << 1));
    if (x) ans = min(ans, d[a][b][c][2] + (x - 1 << 1) + (y - 1 << 1));
    return ans;
}

int main() {
    init();
    while (cin >> op >> a >> b) {
        int c = op == 'U' ? 0 : op == 'V' ? 1 : 2;
        if (a > b) swap(a, b), c = c == 0 ? 0 : c == 1 ? 2 : 1;
        cout << calc(c) << '\n';
    }
    return 0;
}

⭐算乘方的牛

bool dfs(int u, int a, int b) {
    if (a == p || b == p) return true;
    if (a > b) swap(a, b);
    if (!u || b << u < p || p % __gcd(a, b)) return false;
    if (st.count({u, {a, b}})) return false; st.insert({u, {a, b}});
    if (dfs(u - 1, a << 1, b)) return true;
    if (a != b && dfs(u - 1, a, a << 1)) return true;
    if (a != b && dfs(u - 1, a, b << 1)) return true;
    if (a != b && dfs(u - 1, b, b << 1)) return true;
    if (a != b && dfs(u - 1, a, a + b)) return true;
    if (a != b & dfs(u - 1, b, a + b)) return true;
    if (a != b && dfs(u - 1, b - a, a))  return true;
    if (a != b && dfs(u - 1, b - a, b)) return true;
    return false;
}

int main() {
    cin >> p;
    while (!dfs(ans, 1, 0)) ++ans;
    cout << ans;
    return 0;
}

涂满它

int f() {
    int tot = 0, s = 0;
    rep (i, 1, n) rep (j, 1, n) if (v[i][j] != 1 && !(s & mp[i][j])) ++tot, s ^= mp[i][j];
    return tot;
}

void draw(int a, int b, int c) {
    v[a][b] = 1;
    rep (i, 0, 3) {
        int x = a + d[i][0], y = b + d[i][1];
        if (x < 1 || x > n || y < 1 || y > n || v[x][y] == 1) continue;
        if (mp[x][y] == c) draw(x, y, c);
        else v[x][y] = 2;
    }
}

bool check(int c) {
    bool flag = 0;
    rep (i, 1, n) rep (j, 1, n) if (mp[i][j] == c && v[i][j] == 2) draw(i, j, c), flag = 1;
    return flag;
}

bool dfs(int u) {
    int ff = f();
    if (ff == 0) return true;
    if(u + ff > ans) return false;
    rep (i, 0, 5) {
        int cur[9][9]; memcpy(cur, v, sizeof v);
        if (!check(1 << i)) continue;
        if (dfs(u + 1)) return true;
        memcpy(v, cur, sizeof v);
    }
    return false;
}

int main() {
    while (cin >> n, n) {
        memset(v, 0, sizeof v); ans = 0;
        rep (i, 1, n) rep(j, 1, n) cin >> mp[i][j], mp[i][j] = 1 << mp[i][j];
        draw(1, 1, mp[1][1]);
        while (!dfs(0)) ++ans;
        cout << ans << '\n';
    }    
    return 0;
} 

骑士精神

int f() {
    int x = 0, y = 0;
    for (int i = 1; i < 5; ++i) for (int j = 0; j < b[i]; ++j)
        x += a[i][j] != 0, y += a[4 - i][4 - j] != 1;
    return x + y;
}

int dfs(int n, int x, int y, int las) {
    int ff = f();
    if (!ff) return 1;
    if (n - ff < 0) return 0;
    for (int i = 0; i < 8; ++i) {
        int dx = x + d[i][0], dy = y + d[i][1];
        if (min(dx, dy) < 0 || max(dx, dy) > 4 || abs(las - i) == 4) continue;
        swap(a[dx][dy], a[x][y]);
        if (dfs(n - 1, dx, dy, i)) return 1;
        swap(a[dx][dy], a[x][y]);
    }
    return 0;
}

int main() {
    for (cin >> _; _; --_) {
        int x, y, ans = 0;
        for (int i = 0; i < 5; ++i) for (int j = 0; j < 5; ++j) {
            char c; cin >> c, a[i][j] = c ^ '0';
            if (c == '*') x = i, y = j;
        }
        while (ans <= 15 && !dfs(ans, x, y, -5)) ++ans;
        cout << (ans > 15 ? -1 : ans) << '\n';
    }
    return 0;
}

数学知识

质数

⭐质数的距离

int main() {
    init(1e6);
    while (cin >> L >> R) {
        memset(v, 0, sizeof v); L = max(L, 2);
        rep (i, 1, tot)
            for (ll j = max(((L - 1) / prime[i] + 1ll) * prime[i], prime[i] << 1); j <= R; j += prime[i])
                v[j - L] = 1;
        int mx = -1, mi = 2e9, a, b, x, y;
        for (int i = 0, j = -1;; j = i++) {
            while (v[i] && i <= R - L) ++i;
            if (i > R - L) break;
            if (j == -1) continue;
            if (i - j > mx) mx = i - j, a = j + L, b = i + L;
            if (i - j < mi) mi = i - j, x = j + L, y = i + L;
        }
        if (mx == -1) cout << "There are no adjacent primes.\n";
        else cout << x << "," << y << " are closest, " << a << "," << b << " are most distant.\n";
    }
    return 0;
}

阶乘分解

void work(int n) {
    for (int i = 2; i <= n; ++i) {
        if (!v[i]) {
            int cnt = 0; prime[++tot] = i;
            for (long long j = i; j <= n; j *= i) cnt += n / j;
            cout << i << ' ' << cnt << '\n';
        }
        for (int j = 1; j <= tot && prime[j] <= n / i; ++j) {
            v[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
}

int main() {
    int n; cin >> n; work(n);
    return 0;
}

约数

反素数

void dfs(int depth, ll temp, int num, int las) {
    if (depth > 15 || temp > n) return;
    if (ans_num < num) ans = temp, ans_num = num;
    if (ans_num == num && ans > temp) ans = temp;
    for (int i = 1; i <= las; ++i) {
        if (temp * p[depth] > n) break;
        dfs(depth + 1, temp *= p[depth], num * (i + 1), i);
    }
}

int main() {
    cin >> n; dfs(0, 1, 1, 32);
    cout << ans;
    return 0;
}

⭐余数之和

#include <bits/stdc++.h>
using namespace std;

int main() {
    long long n, m, ans; cin >> n >> m; ans = n * m; n = min(m, n);
    for (int l = 1, r; l <= n; l = r + 1)
        r = min(m / (m / l), n), ans -= m / l * (l + r) * (r - l + 1) >> 1;
    cout << ans;
    return 0;
}

⭐Hankson的趣味题

unordered_map<int, int> solve(int n) {
    unordered_map<int, int> st;
    for (int i = 2; i <= n / i; ++i) if (n % i == 0)
        while (n % i == 0) ++st[i], n /= i;
    if (n > 1) st[n] = 1;
    return st;
}

int main() {
    for (cin >> _; _; --_) {
        cin >> a0 >> a1 >> b0 >> b1;
        unordered_map<int, int> a[2] = {solve(a0), solve(a1)}, b[2] = {solve(b0), solve(b1)};
        long long ans = 1;
        for (auto& i : b[1]) {
            int x = a[0][i.first], z = a[1][i.first], y = b[0][i.first];
            if (x > z && y < i.second && i.second == z) continue;
            else if (x > z && y == i.second && z <= i.second) continue;
            else if (x == z && y < i.second && z <= i.second) continue;
            else if (x == z && y == i.second && z <= i.second) ans *= i.second - z + 1;
            else { ans = 0; break; }
        }
        cout << ans << '\n';
    }    
    return 0;
}

可见的点

void euler(int n) {
    for (int i = 1; i <= n; ++i) phi[i] = i;
    for(int i = 2; i <= n; ++i) if(phi[i] == i)
        for (int j = i; j <= n; j += i) phi[j] = phi[j] / i * (i - 1);
}

int main() {
    euler(1000);
    for (cin >> _; _; --_, ans = 0) {
        cin >> n;
        for (int i = 2; i <= n; ++i) ans += phi[i] << 1;
        cout << ++cas << ' ' << n << ' ' << (n ? ans + 3 : 0) << '\n';
    }
    return 0;
}

同余

最幸运的数字

ll mul(ll a, ll b, ll p) {
    return (a * b - (ll)((double)a * b / p) * p) % p;
}

ll qpow(ll a, ll b, ll p) {
    ll ans = 1;
    for(; b; b >>= 1, a = mul(a, a, p)) if (b & 1) ans = mul(ans, a, p);
    return ans;
}

ll getphi(ll n)  {
    ll ans = n;
    for (int i = 2; i <= n / i; ++i) if (n % i == 0) {
        ans = ans / i * (i - 1);
        while (n % i == 0) n /= i;
    }
    return n - 1 ? ans / n * (n - 1) : ans;
}

ll work(ll x, ll p)  {
    ll ans = 2e18;
    if (__gcd(p, 10ll) != 1) return 0;
    for (int i = 1; i <= x / i; ++i) if (x % i == 0) {
        if (qpow(10, i, p) == 1) ans = min(ans, (ll)i);
        if (qpow(10, x / i, p) == 1) ans = min(ans, x / i);
    }
    return ans == 2e18 ? 0 : ans;
}

int main() {
    ll b, cas = 0;
    while (scanf("%lld", &b), b) {
        b = b / __gcd(b, 8ll) * 9;
        printf("Case %lld: %lld\n", ++cas, work(getphi(b), b)); 
    }
    return 0;
}

同余方程

ll exgcd(ll a, ll b, ll& x, ll& y) {
    if (!b) { x = 1, y = 0; return a; }
    ll d = exgcd(b, a % b, y, x); y -= a / b * x;
    return d;
}

int main() {
    ll a, b, x, y; cin >> a >> b;
    ll d = exgcd(a, b, x, y);
    cout << (x + b / d) % (b / d);
    return 0;
}

表达整数的奇怪方式

ll exgcd(ll a, ll b, ll& x, ll& y) {
    if (b == 0) { x = 1, y = 0; return a; }
    ll d = exgcd(b, a % b, y, x); y -= a / b * x;
    return d;
}

ll mod(ll x, ll p) { return (x % p + p) % p; }

ll work(ll a[], ll m[], int n) {
    for (int i = 2; i <= n; ++i) {
        ll k1, k2, d = exgcd(m[1], m[i], k1, k2);
        ll c = mod(a[i] - a[1], m[i]);
        if (c % d) return -1;
        ll p = m[i] / d; c = c / d % p; k1 = mod(k1 * c, p);
        a[1] += m[1] * k1; m[1] = m[1] / d * m[i];
    }
    return a[1];
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> m[i] >> a[i];
    cout << work(a, m, n);
    return 0;
}

矩阵乘法

斐波那契

struct Matrix {
    static const int mod = 10000;
    int n, m;
    vector<vector<int>> a;
    Matrix(int N = 0, int M = 0): n(N), m(M) { a.resize(n, vector<int>(m)); }
    Matrix operator*(const Matrix& x) {
        Matrix ans(n, x.m);
        for (int i = 0; i < n; ++i) for (int j = 0; j < x.m; ++j)
            for (int k =0; k < m; ++k) ans.a[i][j] = (ans.a[i][j] + a[i][k] * x.a[k][j]) % mod;
        return ans;
    }
};

Matrix qpow(Matrix a, int b) {
    Matrix ans(a.n, a.m);
    for (int i = 0; i < a.n; ++i) ans.a[i][i] = 1;
    for (; b; b >>= 1, a = a * a) if (b & 1) ans = ans * a;
    return ans;
}

int main() {
    int n;
    while (cin >> n, n > -1) {
        Matrix a(2, 2); a.a[0][1] = a.a[1][0] = a.a[1][1] = 1;
        a = qpow(a, n); cout << a.a[1][0] << '\n';
    }
    return 0;
}

⭐石头游戏

struct Matrix {
    int n, m;
    vector<vector<long long>> a;
    Matrix(int N = 0, int M = 0): n(N), m(M) { a.resize(n, vector<long long>(m)); }
    Matrix operator*(const Matrix& x) {
        Matrix ans(n, x.m);
        for (int i = 0; i < n; ++i) for (int j = 0; j < x.m; ++j)
            for (int k = 0; k < m; ++k) ans.a[i][j] += a[i][k] * x.a[k][j];
        return ans;
    }
};

Matrix qpow(Matrix a, int b) {
    Matrix ans(a.n, a.m);
    for (int i = 0; i < a.n; ++i) ans.a[i][i] = 1;
    for (; b; b >>= 1, a = a * a) if (b & 1) ans = ans * a;
    return ans;
}

Matrix a[61];
int c[9][9], len[10], n, m, t, act;
char s[10][10];

int main() {
    scanf("%d%d%d%d", &n, &m, &t, &act);
    a[0] = Matrix(n * m + 1, n * m + 1);
    for (int i = 0; i < m * n + 1; ++i) a[0].a[i][i] = 1;
    for (int i = 1; i < 61; ++i) a[i] = a[0];
    for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) scanf("%1d", &c[i][j]);
    for (int i = 0; i < act; ++i) cin >> s[i], len[i] = strlen(s[i]);
    for (int k = 1; k < 61; a[k] = a[k - 1] * a[k], ++k) for (int i = 1; i <= n; ++i)
        for (int j = 1, id = (i - 1) * m + 1; j <= m; ++j, ++id)
            switch (s[c[i][j]][(k - 1) % len[c[i][j]]]) {
            case 'N': if (i > 1) a[k].a[id][id - n] = 1; a[k].a[id][id] = 0; break;
            case 'W': if (j > 1) a[k].a[id][id - 1] = 1; a[k].a[id][id] = 0; break;
            case 'S': if (i < n) a[k].a[id][id + n] = 1; a[k].a[id][id] = 0; break;
            case 'E': if (j < m) a[k].a[id][id + 1] = 1; a[k].a[id][id] = 0; break;
            case 'D': a[k].a[id][id] = 0; break;
            default : a[k].a[0][id] = s[c[i][j]][(k - 1) % len[c[i][j]]] ^ '0';
            }
    a[60] = qpow(a[60], t / 60); a[60] = a[60] * a[t % 60];
    long long mx = 0; for (int i = 1; i < n * m + 1; ++i) mx = max(mx, a[60].a[0][i]);
    printf("%lld", mx);
    return 0;
}

高斯消元与线性空间

球星空间产生器

int gauss(int n, int m) {
    int c = 1, r = 1;
    for (int t = r; c < m && r <= n; ++c, t = r) {
        rep (i, r + 1, n) if (fabs(a[i][c]) > fabs(a[t][c])) t = i;
        if (fabs(a[t][c]) < 1e-6) continue;
        if (t != r) rep (i, 1, m) swap(a[t][i], a[r][i]);
        rep (i, c + 1, m) a[r][i] /= a[r][c]; a[r][c] = 1;
        for (int i = r + 1; i <= n; a[i++][c] = 0) if (a[i][c])
            rep (j, c + 1, m) a[i][j] -= a[i][c] * a[r][j];
        ++r;
    }
    rep (i, r, n) if (a[i][m]) return -1;
    if (r < m) return 0;
    per (i, m - 2, 1) rep (j, i + 1, m - 1) a[i][m] -= a[j][m] * a[i][j];
    return 1;
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[0][i];
    for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) {
        cin >> a[i][j]; a[i][n + 1] += a[i][j] * a[i][j] - a[0][j] * a[0][j];
        a[i][j] = 2 * (a[i][j] - a[0][j]);
    }
    gauss(n, n + 1);
    for (int i = 1; i <= n; ++i) cout << setiosflags(ios::fixed) << setprecision(3) << a[i][n + 1] << ' ';
    return 0;
}

开关问题

int gauss(int n) {
    bitset<29> a[n + 1]; a[0][0] = 1;
    for (int i = 1; i <= n; ++i) cin >> m, a[i][0] = m, a[i][i] = 1;
    for (int i = 1; i <= n; ++i) cin >> m, a[i][0] = a[i][0] ^ m;
    while (cin >> x >> y, x || y) a[y][x] = 1;
    int r = 1;
    for (int c = 1, t = 0; c <= n; ++c, r += !!t, t = 0) {
        for (int j = r; j <= n; ++j) if (a[j][c]) { t = j; break; }
        if (!t) continue; swap(a[r], a[t]);
        for (int j = 1; j <= n; ++j) if (j != r && a[j][c]) a[j] ^= a[r];
    }
    for (int i = n; i >= r; --i) if (a[i] == a[0]) return -1;
    return pow(2, n - r + 1);
}

int main() {
    for (cin >> _; _; --_) {
        cin >> n;
        int ans = gauss(n); 
        if (ans == -1) cout << "Oh,it's impossible~!!\n";
        else cout << ans << '\n';
    }
    return 0;
}

装备购买

int gauss(int n, int m) {
    int c = 1, r = 1;
    for (int t = r; c <= m && r <= n; ++c, t = r) {
        for (int i = r + 1; i <= n; ++i)
            if (fabs(a[i][c]) > eps && a[i][0] < a[t][0] || fabs(a[t][c]) < eps) t = i;
        if (fabs(a[t][c]) < eps) continue;
        if (t != r) for (int i = 0; i <= m; ++i) swap(a[t][i], a[r][i]);
        ans += a[r][0];
        for (int i = c + 1; i <= m; ++i) a[r][i] /= a[r][c];
        a[r][c] = 1;
        for (int i = r + 1; i <= n; ++i) if (a[i][c]) {
            for (int j = c + 1; j <= m; ++j)  a[i][j] -= a[i][c] * a[r][j];
            a[i][c] = 0;
        }
        ++r;
    }
    return r - 1;
}

int main() {
    cin >> n >> m;
    rep (i, 1, n) rep (j, 1, m) cin >> a[i][j];
    rep (i, 1, n) cin >> a[i][0];
    cout << gauss(n, m) << ' ' << ans;
    return 0;
}

⭐异或运算

struct XXJ {
    static const int N = 59;
    ll g[N + 1]; bool zero = 0; vector<ll> a;
    void init() { memset(g, 0, sizeof g); zero = 0; }
    void insert(ll x) {
        per (i, N, 0) if (x >> i & 1) { if (!g[i]) {g[i] = x; return; }; x ^= g[i]; } zero = 1;
    }
    bool isin(ll x) { per (i, N, 0) if (x >> i & 1) x ^= g[i]; return !x; }
    ll max() { ll mx = 0; per (i, N, 0) umax(mx, mx ^ g[i]); return mx; }
    ll min() { if (zero) return 0; rep (i, 0, N) if (g[i]) return g[i]; }
    void build() {
        vector<ll>().swap(a);
        rep (i, 0, N) {
            per (j, i - 1, 0) if (g[i] >> j & 1) g[i] ^= g[j];
            if (g[i]) a.emplace_back(g[i]);
        }
    }
    ll kth(ll k) {
        ll ans = 0; k -= zero; if (k <= 0) return !k ? 0 : -1;
        if (k >> a.size()) return -1;
        rep (i, 0, a.size() - 1) if (k >> i & 1) ans ^= a[i];
        return ans;
    }
} xxj;

int main() {
    for (cin >> _; _; --_) {
        cout << "Case #" << ++cas << ":\n"; cin >> n; xxj.init(); 
        for (int i = 0; i < n; ++i) cin >> k, xxj.insert(k); xxj.build();
        for (cin >> m; m; --m) cin >> k, cout << xxj.kth(k) << '\n';
    }
    return 0;
}

组合计数

计算系数

int qpow(long long a, int b) {
    long long ans = 1;
    for (; b; b >>= 1, a = a * a % mod) if (b & 1) ans = ans * a % mod;
    return ans;
}

void init(int n) {
    fac[0] = fac[1] = inv[0] = inv[1] = facinv[0] = facinv[1] = 1;
    for (int i = 2; i <= n; ++i) {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
        facinv[i] = facinv[i - 1] * inv[i] % mod; 
    }
}

int main() {
    cin >> a >> b >> k >> n >> m; init(k);
    int ans = qpow(a, n) * qpow(b, m) % mod * fac[k] % mod * facinv[n] % mod * facinv[k - n] % mod;
    cout << ans;
    return 0;
}

计数交换

int power(ll a, int b) {
    ll ans = 1;
    for (; b; b >>= 1, a = a * a % mod) if (b & 1) ans = ans * a % mod;
    return ans;
}

void dfs(int u, int cnt) {
    v[u] = 1;
    if (v[a[u]]) ans = (ll)ans * d[cnt] % mod * c[cnt - 1] % mod;
    else dfs(a[u], cnt + 1);
}

void init () {
    c[0] = b[0] = d[1] = b[1] = c[1] = 1;
    for (int i = 2; i <= 1e5; ++i) {
        b[i] = (ll)b[i - 1] * i % mod;
        c[i] = power(b[i], mod - 2);
        d[i] = power(i, i - 2);
    }
}

int main() {
    IOS; init();
    for (cin >> _; _; --_) {
        cin >> n; cnt = 0, ans = 1;
        for (int i = 1; i <= n; ++i) cin >> a[i], v[i] = 0;
        for (int i = 1; i <= n; ++i) if (!v[i]) dfs(i, 1), ++cnt;
        ans = (ll)ans * b[n - cnt] % mod;
        cout << ans << '\n';
    }
    return 0;
}

⭐古代猪文

ll fac[4][N], inv[4][N], facinv[4][N], n, q, a[5], m[5];
int pri[N * 10], tot;

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

ll C(int n, int m, int k) {
    return m > n ? 0 : fac[k][n] * facinv[k][m] % mod[k] * facinv[k][n - m] % mod[k];
}

ll lucas(ll n, ll m, int k) {
    return m ? C(n % mod[k], m % mod[k], k) * lucas(n / mod[k], m / mod[k], k) % mod[k] : 1;
}

void init(int k) {
    fac[k][0] = fac[k][1] = inv[k][0] = inv[k][1] = facinv[k][0] = facinv[k][1] = 1;
    for (int i = 2; i <= mod[k]; ++i) {
        fac[k][i] = fac[k][i - 1] * i % mod[k];
        inv[k][i] = (mod[k] - mod[k] / i) * inv[k][mod[k] % i] % mod[k];
        facinv[k][i] = facinv[k][i - 1] * inv[k][i] % mod[k];
    }
}

ll work(ll a[], ll m[], int n) {
    ll t = 1, ans = 0;
    for (int i = 1; i <= n; ++i) t *= m[i];
    for (int i = 1; i <= n; ++i) {
        ll cur = qpow(t / m[i], m[i] - 1, t) * a[i] % t;
        ans = (ans + cur) % t;
    }
    return ans;
}

int main() {
    for (int i = 0; i < 4; ++i) init(i), m[i + 1] = mod[i];
    cin >> n >> q;
    if (q == mod[4]) return cout << 0, 0;
    for (int i = 1; i <= n / i; ++i) if (n % i == 0) {
        pri[++tot] = i;
        if (n / i != i) pri[++tot] = n / i;
    }
    for (int i = 0; i < 4; ++i) for (int j = 1; j <= tot; ++j)
        a[i + 1] = (a[i + 1] + lucas(n, pri[j], i)) % mod[i];
    ll ans = work(a, m, 4);
    cout << qpow(q % mod[4], ans, mod[4]);
    return 0;
}

容斥原理和Mobius函数

⭐Devu和鲜花

nt qpow(ll a, int b) {
    ll ans = 1;
    for (; b; b >>= 1, a = a * a % mod) if (b & 1) ans = ans * a % mod;
    return ans;
}

int C(int n, int m) {
    if (m > n) return 0;
    ll ans = b[m];
    for (int i = 1; i <= m; ++i) ans = ans * (n + 1 - i) % mod;
    return ans;
}

int lucas(ll n, int m) {
    return m ? (ll)C(n % mod, m) * lucas(n / mod, m / mod) % mod : 1; 
}

int main() {
    cin >> n >> m >> a[1];
    for (int i = 2; i <= n; ++i) {
        cin >> a[i];
        inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod;
        b[i] = (ll)b[i - 1] * inv[i] % mod;
    }
    for (int i = 0; i < 1 << n; ++i) {
        int p = 0;
        ll t = n + m;
        for (int j = 0; j < n; ++j) if (i >> j & 1) ++p, t -= a[j + 1];
        k = (k + (p & 1 ? -1 : 1) * lucas(t - p - 1, n - 1)) % mod;
    }
    cout << (k + mod) % mod;
    return 0;
}

破译密码

void init(int n) {
    mu[1] = s[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (!v[i]) pri[++tot] = i, mu[i] = -1;
        s[i] = s[i - 1] + mu[i];
        for (int j = 1; j <= tot && pri[j] <= n / i; ++j) {
            v[i * pri[j]] = 1;
            if (i % pri[j] == 0) break;
            mu[i * pri[j]] = -mu[i];
        }
    }
}

int main() {
    init(5e4);
    for (cin >> _; _; --_) {
        cin >> a >> b >> d; a /= d, b /= d;
        long long ans = 0;
        if (a > b) swap(a, b);
        for (int l = 1, r; l <= a; l = r + 1) {
            r = min(a, min(a / (a / l), b / (b / l)));
            ans += (long long)(s[r] - s[l - 1]) * (a / l) * (b / l);
        }
        cout << ans << '\n';
    }
    return 0;
}

概率与数学期望

Rainbow的信号

void work(int k) {
    int c1 = 0, c2 = 0, last[2] = {0};
    double pos = 1.0 * (1 << k) / n / n;
    for (int r = 1; r <= n; ++r) {
        bool v = ((a[r] >> k) & 1);
        if (v) {
            ansxor += pos, ansand += pos, ansor += pos;
            ansor += pos * (r - 1) * 2;
            ansand += pos * (r - 1 - last[0]) * 2;
            ansxor += pos * c1 * 2;
        } else {
            ansor += pos * last[1] * 2;
            ansxor += pos * c2 * 2;
        }
        ++c1;
        if (v) swap(c1, c2); 
        last[v] = r;
    }
}

int main() {
   cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 0; i < 32; ++i) work(i);
    printf("%.3lf %.3lf %.3lf", ansxor, ansand, ansor);
    return 0;
}

⭐绿豆蛙的归宿

int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; ++i) {
        int u, v, c; cin >> u >> v >> c;
        add(v, u, c); ++a[u], ++b[u];
    }
    queue<int> q; q.push(n);
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = h[x]; i; i = ne[i]) {
            int y = to[i];
            dis[y] += (dis[x] + co[i]) / b[y];
            if (--a[y] == 0) q.push(y);
        }
    }
    cout << setiosflags(ios::fixed) << setprecision(2) << dis[1];
    return 0;
}

⭐扑克牌

double dfs(int a, int b, int c, int d, int e, int g) {
    if (f[a][b][c][d][e][g] > eps) return f[a][b][c][d][e][g];
    if (a + (e == 1) + (g == 1) >= A && b + (e == 2) + (g == 2) >= B &&
       c + (e == 3) + (g == 3) >= C && d + (e == 4) + (g == 4) >= D) return 0;
    double sum = 1.0, x = 1e9, y = 1e9;
    int s = 54 - a - b - c - d - (e != 0) - (g != 0);
    if (a < 13) sum += dfs(a + 1, b, c, d, e, g) * (13 - a) / s;
    if (b < 13) sum += dfs(a, b + 1, c, d, e, g) * (13 - b) / s;
    if (c < 13) sum += dfs(a, b, c + 1, d, e, g) * (13 - c) / s;
    if (d < 13) sum += dfs(a, b, c, d + 1, e, g) * (13 - d) / s;
    if (e == 0) for (int i = 1; i < 5; ++i) x = min(x, dfs(a, b, c, d, i, g) / s);
    if (g == 0) for (int i = 1; i < 5; ++i) y = min(y, dfs(a, b, c, d, e, i) / s);
    if (e == 0) sum += x;
    if (g == 0) sum += y;
    return f[a][b][c][d][e][g] = sum;
}

int main() {
    cin >> A >> B >> C >> D;
    if (max(A - 13, 0) + max(B - 13, 0) + max(C - 13, 0) + max(D - 13, 0) > 2) 
        cout << setiosflags(ios::fixed) << setprecision(3) << -1.00;
    else cout << setiosflags(ios::fixed) << setprecision(3) << dfs(0, 0, 0, 0, 0, 0);
    return 0;
}

0/1分数规划

博弈论之SG函数

⭐剪纸游戏

void init() {
    for (int n = 2; n <= 200; ++n) for (int m = 2; m <= 200; ++m) {
        memset(mex, 0, sizeof mex);
        for (int i = 2; n - i >= 2; ++i)  mex[sg[i][m] ^ sg[n - i][m]] = 1;
        for (int j = 2; m - j >= 2; ++j)  mex[sg[n][j] ^ sg[n][m - j]] = 1;
        for (int i = 0; i <= 200; ++i) if (!mex[i]) { sg[n][m] = i; break; }
    }
}

int main() {
    init();
    while (cin >> n >> m) cout << (sg[n][m] ? "WIN\n" : "LOSE\n");
    return 0;
}

总结与练习

最大公约数

int init(int n) {
    for (int i = 2; i <= n; ++i) {
        if (!v[i]) pri[++tot] = i, phi[i] = i - 1;
        s[i] = s[i - 1] + phi[i];
        for (int j = 1; j <= tot && pri[j] <= n / i; ++j) {
            v[pri[j] * i] = 1;
            phi[pri[j] * i] = phi[i] * (pri[j] - (i % pri[j] != 0));
            if (i % pri[j] == 0) break;
        }
    }
}

int main() {
    cin >> n; init(n);
    for (int i = 1; i <= tot; ++i) ans += s[n / pri[i]] << 1 | 1;
    cout << ans;
    return 0;
}

龙哥的问题

int phi(int n) {
    int ans = n;
    for (int i = 2; i <= n / i; ++i) if (n % i == 0) {
        ans = ans / i * (i - 1);
        while (n % i == 0) n /= i;
    }
    return n - 1 ? ans / n * (n - 1) : ans;
}

int main() {
    long long ans = 0, n; cin >> n;
    for (int i = 1; i <= n / i; ++i) if (n % i == 0) {
        ans += (long long)i * phi(n / i);
        if (i != n / i) ans += (long long)n / i * phi(i);
    }
    cout << ans;
    return 0;
}

青蛙的约会

ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) return x = 1, y = 0, a;
    ll d = exgcd(b, a % b, y, x); y -= a / b * x;
    return d;
}

int main() {
    cin >> x >> y >> m >> n >> L; d = exgcd(m - n, -L, a, b);
    if ((y - x) % d) return cout << "Impossible", 0;
    a = ((y - x) / d * a % abs(L / d) + abs(L / d)) % abs((L / d));
    return cout << a, 0;
}

阿九大战朱最学

ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) return x = 1, y = 0, a;
    ll d = exgcd(b, a % b, y, x); y -= a / b * x;
    return d;
}

ll CRT(ll a[], ll m[], int n) {
    ll t = 1, ans = 0;
    for (int i = 0; i < n; ++i) t *= m[i];
    for (int i = 0; i < n; ++i) {
        ll cur = t / m[i], x, y; exgcd(cur, m[i], x, y);
        ans = (ans + a[i] * cur % t * x % t) % t;
    }
    return (ans + t) % t;
}

int main() {
    cin >> n; for (int i = 0; i < n; ++i) cin >> a[i] >> b[i];
    cout << CRT(b, a, n);
    return 0;
}

计算器

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

ll exgcd(ll a, ll b, ll &x, ll &y)  {
    if (!b) return x = 1, y = 0, a;
    ll d = exgcd(b, a % b, y, x); y -= a / b * x;
    return d;
}

ll baby_step_giant_step(ll a, ll b, ll p) { //a^x%p=b (gcd(a, p)=1)
    unordered_map<ll, ll> st; b %= p;
    int t = sqrt(p - 1) + 1; ll cur = 1;
    for (int i = 0; i < t; ++i, cur = cur * a % p) st[b * cur % p] = i;
    a = qpow(a, t, p); cur = a;
    if (a == 0) return b == 0 ? 1 : -1;
    for (int i = 1; i <= t; ++i, cur = cur * a % p) {
        ll c = st.count(cur) ? st[cur] : -1;
        if (c >= 0 && i * t - c >= 0) return i * t - c;
    }    
    return -1;
}

int main() {
    cin >> n >> k;
    for (int i = 0; i < n; ++i) {
        cin >> a >> b >> c;
        if (k == 1) cout << qpow(a, b, c) << '\n';
        else if (k == 2) {
            ll x, y, d = exgcd(a, c, x, y); y = abs(c / d);
            if (b % d) cout << "Orz, I cannot find x!\n";
            else cout << (x % y * b / d % y + y) % y << '\n';
        } else {
            ll ans = baby_step_giant_step(a, b, c);
            if (ans == -1) cout << "Orz, I cannot find x!\n";
            else cout << ans << '\n';
        }
    }
    return 0;
}

矩阵幂求和

struct matrix {
    long long n, m, a[30][30];
    matrix(int N = 0, int M = 0) : n(N), m(M) { memset(a, 0, sizeof a);  };
    matrix operator*(const matrix& b) {
        matrix ans(n, b.m);
        for (int i = 0; i < n; ++i) for (int j = 0; j < b.m; ++j)
            for (int k = 0; k < m; ++k) ans.a[i][j] = (ans.a[i][j] + a[i][k] * b.a[k][j] % p) % p;
        return ans;
    }
    matrix operator+(const matrix& b) {
        matrix ans(n, m);
        for (int i = 0; i < n; ++i) for (int j = 0; j < b.m; ++j) ans.a[i][j] = (a[i][j] + b.a[i][j]) % p;
        return ans;
    }
} a;

matrix qpow(matrix a, int b) {
    matrix ans(a.n, a.m);
    for (int i = 0; i < a.n; ++i) ans.a[i][i] = 1;
    for (; b; b >>= 1, a = a * a) if (b & 1) ans = ans * a;
    return ans;
}

matrix work(int len, matrix x) {
    if (len == 1) return x;
    matrix ans = work(len >> 1, x), t = qpow(x, len >> 1);
    ans = ans + t * ans;
    return len & 1 ? ans + qpow(x, len) : ans;
}

int main() {
    cin >> n >> k >> p; a = matrix(n, n);
    for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) cin >> a.a[i][j];
    a = work(k, a);
    for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) cout << a.a[i][j] << " \n"[j == n - 1];
    return 0;
}

⭐矩阵

struct Matrix {
    int n; vector<vector<ll>> a;
    Matrix(int x = 0): n(x) { vector<vector<ll>>(x, vector<ll>(x, 0ll)).swap(a); }
    Matrix operator*(const Matrix& y) {
        Matrix ans(n);
        for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j)
            for (int k = 0; k < n; ++k) ans.a[i][j] = (ans.a[i][j] + a[i][k] * y.a[k][j] % mod) % mod;
        return ans;
    }
} t;

Matrix qpow(Matrix a, int b) {
    Matrix ans(a.n);
    for (int i = 0; i < a.n; ++i) ans.a[i][i] = 1;
    for (; b; a = a * a, b >>= 1) if (b & 1) ans = ans * a;
    return ans;
}

int main() {
    while (cin >> n >> m) {
        for (int i = 1; i <= n; ++i) cin >> a[i]; a[0] = 23; a[n + 1] = 3;
        t = Matrix(n + 2);
        for (int j = 0; j <= n; ++j) {
            t.a[0][j] = 10;
            for (int i = 1; i <= j; ++i) t.a[i][j] = 1;
        }
        for (int j = 0; j <= n + 1; ++j) t.a[n + 1][j] = 1;
        t = qpow(t, m); ll ans = 0;
        for (int i = 0; i <= n + 1; ++i) ans = (ans + t.a[i][n] * a[i] % mod) % mod;
        cout << ans << '\n';
    }
    return 0;
}

小部件场

int calc(char a, char b) {
    switch (a) {
        case 'M' : return 0;
        case 'T' : return b == 'U' ? 1 : 3;
        case 'W' : return 2;
        case 'F' : return 4;
        default : return b == 'A' ? 5 : 6; 
    }
}

int qpow(int a, int b) {
    int ans = 1;
    for (; b; b >>= 1, a = a * a % mod)
        if (b & 1) ans = ans * a % mod;
    return ans;
}

int gauss(int n, int m) {
    int c = 1, r = 1;
    for (int t = -1; c < m && r <= n; ++c, t = -1) {
        rep (i, r, n) if (a[i][c]) { t = i; break; }
        if (t == -1) continue;
        if (t != r) swap(a[t], a[r]);
        a[r][c] = qpow(a[r][c], mod - 2);
        rep (i, c + 1, m) a[r][i] = (ll)a[r][i] * a[r][c] % mod; a[r][c] = 1;
        for (int i = r + 1; i <= n; a[i++][c] = 0) if (a[i][c])
            rep (j, c + 1, m) a[i][j]=((a[i][j] - a[r][j] * a[i][c])%mod+mod)%mod;
        ++r;
    }
    rep (i, r, n) if (a[i][m]) return -1;
    if (r < m) return 0;
    per (i, m - 1, 1) {
        rep (j, i + 1, m - 1) a[i][m] = ((a[i][m] - a[j][m] * a[i][j]) % mod + mod) % mod;
        if (a[i][m] < 3) a[i][m] += 7;
    }
    return 1;
}

int main() {
    while (scanf("%d%d", &n, &m), n || m) {
        memset(a, 0, sizeof a);
        for (int i = 1; i <= m; ++i) {
            int k, cur; scanf("%d%s%s", &k, s, t);
            while(k--) scanf("%d", &cur), a[i][cur] = (a[i][cur] + 1) % mod;
            a[i][n + 1] = (calc(t[0], t[1]) - calc(s[0], s[1]) + 1 + mod) % mod;
        }
        int t = gauss(m, n + 1);

        if (t == 1) {
            for (int i = 1; i <= n; ++i) printf("%d ", a[i][n + 1]);
            puts("");
        }
        else if (t == 0) puts("Multiple solutions.");
        else puts("Inconsistent data.");
    }
    return 0;
}

⭐异或

struct XXJ {
    static const int N = 59;
    ll g[N + 1];
    void insert(ll x) {
        per (i, N, 0) if (x >> i & 1)
            if (!g[i]) {g[i] = x; return; } else x ^= g[i];
        zero = 1;
    }
    ll max(ll x) { per (i, N, 0) if (!(x >> i & 1)) x ^= g[i]; return x; }
} xxj;

ll n, m, a[50001];
vector<pair<int, ll>> h[50001];

void dfs(int x) {
    for (auto &y : h[x])
        if (a[y.first] != -1) xxj.insert(a[y.first] ^ a[x] ^ y.second);
        else a[y.first] = a[x] ^ y.second, dfs(y.first);
}

int main() {
    cin >> n >> m; memset(a, -1, sizeof a); a[1] = 0;
    for (int i = 0; i < m; ++i) {
        int u, v; ll c; cin >> u >> v >> c;
        h[u].emplace_back(v, c); h[v].emplace_back(u, c);
    }
    dfs(1); if (a[n] != -1) cout << xxj.max(a[n]);
    return 0;
}

新NIM游戏

bool insert(int x) {
    for (int i = 29; ~i; --i) if (x >> i & 1)
        if (!a[i]) return a[i] = x, 1;
        else x ^= a[i];
    return 0;
}

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) cin >> b[i]; sort(b, b + n);
    for (int i = n - 1; ~i; --i) if (!insert(b[i])) ans += b[i];
    cout << ans;
    return 0;
}

排列计数

ll C(int n, int m) { return m > n ? 0 : fac[n] * facinv[m] % mod * facinv[n - m] % mod; }

void init(int n) {
    for (int i = 0; i < 2; ++i) fac[i] = inv[i] = facinv[i] = 1; f[0] = 1;
    for (int i = 2; i <= n; ++i)
        fac[i] = fac[i - 1] * i % mod,
        inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod,
        facinv[i] = facinv[i - 1] * inv[i] % mod,
        f[i] = (f[i - 2] + f[i - 1]) % mod * (i - 1) % mod;
}

int main() {
    IOS; init(1e6);
    for (cin >> _; _; --_) {
        cin >> n >> m;
        cout << C(n, m) * f[n - m] % mod << '\n';
    }
    return 0;
}

⭐天码

void work(ll x) {
    int tot = 0;
    for (int i = 2; i <= x / i; ++i)  if (x % i == 0) {
        pri[tot++] = i;
        while (x % i == 0) x /= i;
    }
    if (x != 1) pri[tot++] = x;
    for (int i = 0; i < 1 << tot; ++i) {
        int c = 0, k = 1;
        for (int j = 0; j < tot; ++j) if (i >> j & 1) k *= pri[j], ++c;
        cnt[k] = c, ++tax[k];
    }
}

int main() {
    for (int i = 5; i <= 1e4; ++i) c[i] = c[i - 1] * i / (i - 4); 
    while (cin >> n) {
        memset(tax, 0, sizeof tax);
        for (int i = 0; i < n; ++i) cin >> m, work(m);
        ll ans = c[n];
        for (int i = 2; i <= 1e4; ++i) ans += c[tax[i]] * (cnt[i] & 1 ? -1 : 1);
        cout << ans << '\n';
    }
    return 0;
}

⭐守卫者的挑战

int main() {
    cin >> n >> m >> k;
    rep (i ,1, n) cin >> p[i], p[i] /= 100;
    rep (i, 1, n) cin >> a[i];
    f[0][0][N] = 1;
    rep(i, 1, n) rep(j, 1, n) rep(t, -i, n)
        f[i][j][min(t + N + a[i], n + N)] += f[i - 1][j - 1][t + N] * p[i],
        f[i][j - 1][t + N] += f[i - 1][j - 1][t + N] * (1 - p[i]);
    rep(i, m, n) rep(j, max(-k, -n), n) ans += f[n][i][j + N];
    cout << setiosflags(ios::fixed) << setprecision(6) << ans;
    return 0;
}

⭐换教室

int main() {
    cin >> n >> m >> v >> e; memset(d, 0x3f, sizeof d);
    rep (i, 1, v) d[i][i] = 0; rep (i, 1, n) cin >> a[i];
    rep (i, 1, n) cin >> b[i]; rep (i, 1, n) cin >> k[i];
    rep (i, 1, e) {
        int u, v, c; cin >> u >> v >> c;
        d[u][v] = d[v][u] = min(d[u][v], c);
    }
    rep (k, 1, v) rep (i, 1, v) rep (j, 1, v) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    rep (i, 0, n) rep (j, 0, m) f[i][j][0] = f[i][j][1] = 1e9;
    f[1][0][0] = f[1][1][1] = 0;
    rep (i, 2, n) rep (j, 0, min(i, m)) {
        if (j < i) f[i][j][0] = min(f[i - 1][j][0] + d[a[i - 1]][a[i]], f[i - 1][j][1] +
            d[a[i - 1]][a[i]] * (1 - k[i - 1]) + d[b[i - 1]][a[i]] * k[i - 1]);
        if (j) f[i][j][1] = min(f[i - 1][j - 1][0] + d[a[i - 1]][a[i]] * (1 - k[i]) + d[a[i - 1]][b[i]] * k[i],
            f[i - 1][j - 1][1] + (d[a[i - 1]][a[i]] * (1 - k[i - 1]) + d[b[i - 1]][a[i]] * k[i - 1]) * (1 - k[i])
            + (d[a[i - 1]][b[i]] * (1 - k[i - 1]) + d[b[i - 1]][b[i]] * k[i - 1]) * k[i]);
    }
    rep (i, 0, m) ans = min(ans, min(f[n][i][0], f[n][i][1]));
    cout << setiosflags(ios::fixed) << setprecision(2) << ans;
    return 0;
}

放弃测试

bool check(double mid) {
    vector<double> c; double cur = 0;
    for (int i = 0; i < n; ++i) c.emplace_back(100.0 * a[i] - mid * b[i]);
    sort(c.begin(), c.end());
    for (int i = k; i < n; ++i) cur += c[i];
    return cur >= 0;
}

int main() {
    while (cin >> n >> k, n || k) {
        for (int i = 0; i < n; ++i) cin >> a[i];
        for (int i = 0; i < n; ++i) cin >> b[i];
        double l = 0, r = 100;
        while (r - l > 1e-6) {
            double mid = (l + r) / 2;
            if (check(mid)) l = mid;
            else r = mid;
        }
        cout << round(l) << '\n';
    }
    return 0;
}

魔法珠

int work (int x) {
    if(sg[x] != -1) return sg[x];
    vector<int> a(1, 1);
    for (int i = 2; i <= x / i; ++i) if (x % i == 0) {
        a.emplace_back(i);
        if (i * i != x) a.emplace_back(x / i);
    }
    unordered_map<int, bool> v;
    int cur = 0;
    for (int i : a) cur ^= work(i);
    for (int i : a) v[cur ^ work(i)] = 1;
    while (v[++sg[x]]);
    return sg[x];
}

int main() {
    memset(sg, -1, sizeof sg); sg[1] = 0;
    while (cin >> n) {
        int c = 0;
        for (int i = 0; i < n; ++i) cin >> m, c ^= work(m);
        cout <<  (c ? "freda\n" : "rainbow\n");
    }
    return 0;
}

格鲁吉亚和鲍勃

int main() {
    for (cin >> _; _; --_) {
        cin >> n; m = 0;
        for (int i = 1; i <= n; ++i) cin >> a[i];
        sort(a + 1, a + 1 + n);
        for (int i = n; i > 0;) m ^= (a[i--] - a[i--] - 1);
        cout << (m ? "Georgia will win\n" : "Bob will win\n");
    }
    return 0;
}

数据结构进阶

并查集

⭐程序自动分析

int f[N << 1], x[N], y[N], z[N];

int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }

void unit(int x, int y) { x = find(x); y = find(y); if (x != y) f[x] = y; }

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> m; VI c; bool g = 0;
        rep (i, 1, m) cin >> x[i] >> y[i] >> z[i], c.pb(x[i]), c.pb(y[i]);
        sort(all(c)); c.erase(unique(all(c)), c.end()); n = c.size();
        rep (i, 1, n) f[i] = i, f[i + n] = i + n;
        rep (i, 1, m) {
            x[i] = lower_bound(all(c), x[i]) - c.begin() + 1;
            y[i] = lower_bound(all(c), y[i]) - c.begin() + 1;
            if (!z[i] && find(x[i]) == find(y[i])) g = 1;
            else if (!z[i]) unit(x[i] + n, y[i] + n);
            else if (find(x[i]) != find(y[i]) && find(x[i] + n) == find(y[i] + n)) g = 1;
            else unit(x[i], y[i]), unit(x[i] + n, y[i] + n);
        }
        cout << (!g ? "YES\n" : "NO\n");
    }
    return 0;
}

银河英雄传说

int find(int x) {
    if (x == f[x]) return x;
    if (f[x] == f[f[x]]) return f[x];
    int fa = find(f[x]); d[x] += d[f[x]] - 1;
    return f[x] = fa;
}

void unit(int x, int y) {
    x = find(x), y = find(y);
    if (x != y) { d[y] = sz[x] + 1; sz[f[y] = x] += sz[y]; }
}

int main() {
    for (int i = 1; i <= 3e4; ++i) f[i] = i, d[i] = sz[i] = 1;
    for (cin >> _; _; --_) {
        char op; int x, y; cin >> op >> x >> y;
        if (op == 'M') unit(y, x);
        else cout << (find(x) == find(y) ? abs(d[x] - d[y]) - 1 : -1) << '\n';
    }
    return 0;
}

奇偶游戏

int main() {
    cin >> n >> m; vector<int> c;
    for (int i = 0; i < m; ++i) cin >> x[i] >> y[i] >> op + i, c.push_back(--x[i]), c.push_back(y[i]);
    sort(c.begin(), c.end()); c.erase(unique(c.begin(), c.end()), c.end()); n = c.size();
    for (int i = 0; i < n; ++i) f[i] = i, f[i + n] = i + n;
    for (int i = 0; i < m; ++i) {
        x[i] = lower_bound(c.begin(), c.end(), x[i]) - c.begin();
        y[i] = lower_bound(c.begin(), c.end(), y[i]) - c.begin();
        if (op[i] == 'o') {
            if (find(x[i]) == find(y[i])) return cout << i, 0;
            unit(x[i], y[i] + n), unit(x[i] + n, y[i]);
        } else {
            if (find(x[i]) == find(y[i] + n)) return cout << i, 0;
            unit(x[i], y[i]), unit(x[i] + n, y[i] + n);
        }
    }
    return cout << m, 0;
}

食物链

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) f[i] = i, f[i + n] = i + n, f[i + n * 2] = i + 2 * n;
    for (int i = 0; i < m; ++i) {
        int x, y, op; cin >> op >> x >> y;
        if (x > n || y > n) ++c;
        else if (op == 1) {
            if (find(x) == find(y + n) || find(x) == find(y + 2 * n)) ++c;
            else unit(x, y), unit(x + n, y + n), unit(x + 2 * n, y + 2 * n);
        } else {
            if (find(x) == find(y) || find(x + n) == find(y)) ++c;
            else unit(x, y + n), unit(x + n, y + 2 * n), unit(x + 2 * n, y);
        }
    }
    cout << c;
    return 0;
}

树状数组

楼兰图腾

void add(int x, int k) { for (; x <= n; x += x & -x) c[x] += k; }

int ask(int x) { int ans = 0; for (; x; x -= -x & x) ans += c[x]; return ans; }

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) {
        int a, c; cin >> a; c = ask(a); add(a, 1);
        x += c * (a - 1ll - c), y += (long long)(i - c) * (n - a - (i - c));
    }
    cout << y << ' ' << x;
   

一个简单的整数问题

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= m; ++i) {
        char op; int x, y, z; cin >> op >> x;
        if (op == 'Q') cout << a[x] + ask(x) << '\n';
        else cin >> y >> z, add(x, z), add(y + 1, -z);
    }
    return 0;
}

一个简单的整数问题2

ll c[2][N], a[N], sum[N], n, m, x, y, d;
char s[N];

void add(int x, ll k) { for (int i = x; i <= n; i += -i & i) c[0][i] += k, c[1][i] += x * k; }

ll ask(int x) {
    ll p = 0, q = 0, f = x + 1;
    for (; x; x -= -x & x) p += c[0][x], q += c[1][x];
    return p * f - q;
}

ll ask(int l, int r) { return ask(r) - ask(l - 1); }

int main() {
    cin >> n  >> m;
    for (int i = 1; i <= n; ++ i) cin >> a[i], add(i, a[i] - a[i - 1]);
    while(m--) {
        cin >> s >> x >> y;
        if (s[0] == 'Q') cout << ask(y) - ask(x - 1) << '\n';
        else cin >> d, add(x, d), add(y + 1, -d);
    }
    return 0;
}

谜一样的牛

int main() {
    cin >> n;
    for (int i = 2; i <= n; ++i) cin >> a[i], add(i, 1);
    for (int i = n; i; --i) {
        int len = 1, j = n;
        while (len) len = ask(max(j - len, 1)) < a[i] ? len >> 1 : (j = max(j - len, 1), len << 1);
        add(a[i] = j, -1);
    }
    for (int i = 1; i <= n; ++i) cout << a[i] << '\n';
    return 0;
}

线段树

你能回答这些问题吗

#include <bits/stdc++.h>
using namespace std;

struct BIT {
    static const int N = 5e5 + 5;
    struct node { int val[4], l, r; } tr[N << 2];
    void push_up(int rt) {
        tr[rt].val[0] = max(tr[rt << 1].val[0], tr[rt << 1].val[2] + tr[rt << 1 | 1].val[0]);
        tr[rt].val[1] = max(tr[rt << 1 | 1].val[1], tr[rt << 1 | 1].val[2] + tr[rt << 1].val[1]);
        tr[rt].val[2] = tr[rt << 1].val[2] + tr[rt << 1 | 1].val[2];
        tr[rt].val[3] = max(max(tr[rt << 1].val[3], tr[rt << 1 | 1].val[3]), tr[rt << 1 | 1].val[0] + tr[rt << 1].val[1]);
    }
    void build(int rt, int l, int r, int* a) {
        tr[rt].l = l, tr[rt].r = r;
        if (l == r) { tr[rt].val[0] = tr[rt].val[1] = tr[rt].val[2] = tr[rt].val[3] = a[l]; return; }
        int mid = l + r >> 1; build(rt << 1, l, mid, a); build(rt << 1 | 1, mid + 1, r, a);
        push_up(rt);
    }
    void change(int rt, int d, int k) {
        if (tr[rt].l == d && tr[rt].r == d) { tr[rt].val[0] = tr[rt].val[1] = tr[rt].val[2] = tr[rt].val[3] = k; return; }
        int mid = tr[rt].l + tr[rt].r >> 1;
        if (d <= mid) change(rt << 1, d, k);
        else if (d > mid) change(rt << 1 | 1, d, k);
        push_up(rt);
    }
    node ask(int rt, int l, int r) {
        if (tr[rt].l >= l && tr[rt].r <= r) return tr[rt];
        int mid = tr[rt].l + tr[rt].r >> 1;
        if (mid >= r) return ask(rt << 1, l, r);
        if (mid < r && l > mid) return ask(rt << 1 | 1, l, r);
        auto resb = ask(rt << 1 | 1, l, r), resa = ask(rt << 1, l, r); node ans;
        ans.val[0] = max(resa.val[0], resa.val[2] + resb.val[0]);
        ans.val[1] = max(resb.val[1], resb.val[2] + resa.val[1]);
        ans.val[3] = max(max(resa.val[3], resb.val[3]), max(ans.val[0], ans.val[1]));
        ans.val[3] = max(ans.val[3], resb.val[0] + resa.val[1]);
        ans.val[2] = resa.val[2] + resb.val[2];
        return ans;
    }
} bit;

int a[500001], n, m;

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> a[i]; bit.build(1, 1, n, a);
    for (int i = 0; i < m; ++i) {
        int op, x, y; cin >> op >> x >> y;
        if (op == 1) cout << bit.ask(1, min(x, y), max(x, y)).val[3] << '\n';
        else bit.change(1, x, y);
    }
    return 0;
}

区间最大公约数

struct BIT {
    static const int N = 5e5 + 5;
    struct node {int l, r; long long val; } tr[2000005];
    void push_up(int rt) { tr[rt].val = __gcd(tr[rt << 1].val, tr[rt << 1 | 1].val); }
    void build(int rt, int l, int r, long long* a) {
        tr[rt].l = l, tr[rt].r = r;
        if (l == r) { tr[rt].val = a[l] - a[l - 1]; return; }
        int mid = l + r >> 1;
        build(rt << 1, l, mid, a); build(rt << 1 | 1, mid + 1, r, a);
        push_up(rt);
    }
    void change(int rt, int d, long long k) {
        if (d > tr[rt].r || d < tr[rt].l) return;
        if (tr[rt].l == d && tr[rt].r == d) { tr[rt].val += k; return; }
        int mid = tr[rt].l + tr[rt].r >> 1;
        if (d <= mid) change(rt << 1, d, k);
        else if (d > mid) change(rt << 1 | 1, d, k);
        push_up(rt);
    }
    long long ask(int rt, int l, int r) {
        if (tr[rt].l >= l && tr[rt].r <= r) return tr[rt].val;
        int mid = tr[rt].l + tr[rt].r >> 1;
        if (r <= mid) return ask(rt << 1 , l, r);
        if (l > mid) return ask(rt << 1 | 1, l, r);
        return __gcd(ask(rt << 1, l, r), ask(rt << 1 | 1, l, r));
    }
} bit;

int n, m;
long long a[500005], d, c[500005];

void add(int x, long long k) { for (; x <= n; x += -x & x) c[x] += k; }

long long ask(int x) { long long ans = 0; for (; x; x -= -x & x) ans += c[x]; return ans; }

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> a[i]; bit.build(1, 1, n, a);
    for (int i = 0; i < m; ++i) {
        char op; int x, y; cin >> op >> x >> y;
        if (op == 'C') cin >> d, bit.change(1, x, d), bit.change(1, y + 1, -d), add(x, d), add(y + 1, -d);
        else cout << (x < y ? abs(__gcd(bit.ask(1, x + 1, y), a[x] + ask(x))) : a[x] + ask(x)) << '\n';
    }
    return 0;
}

亚特兰蒂斯

struct BIT {
    struct Node { int l, r, cnt; double len, val; } tr[160005];
    void push_up(int rt) { tr[rt].val =  tr[rt].cnt ? tr[rt].val = tr[rt].len : tr[rt << 1].val + tr[rt << 1 | 1].val; }
    void build(int rt, int l, int r, vector<double>& a) {
        tr[rt] = { l, r, 0, a[r] - a[l], 0.0 };
        if (l + 1 == r) return;
        int mid = l + r >> 1;
        build(rt << 1, l, mid, a); build(rt << 1 | 1, mid, r, a);
    }
    void change(int rt, int l, int r, int k) {
        if (tr[rt].l >= l && tr[rt].r <= r) { tr[rt].cnt += k; push_up(rt); return; }
        int mid = tr[rt].l + tr[rt].r >> 1;
        if (mid > l) change(rt << 1, l, r, k);
        if (mid < r) change(rt << 1 | 1, l, r, k);
        push_up(rt);
    }
} bit;

int n, _, cas;
vector<double> x;

struct line { double x1, x2, y; int k; } a[20000];

int getId(double a) { return lower_bound(x.begin(), x.end(), a) - x.begin(); }

int main() {
    while (cin >> n, n) {
        vector<double>(1, -1.0).swap(x); double ans = 0;
        for (int i = 0; i < n; ++i) {
            double x1, x2, y1, y2; cin >> x1 >> y1 >> x2 >> y2; x.push_back(x1); x.push_back(x2);
            a[i << 1] = { x1, x2, y1, 1}, a[i << 1 | 1] = { x1, x2, y2, -1 };
        }
        sort(x.begin(), x.end()); x.erase(unique(x.begin(), x.end()), x.end());
        sort(a, a + n * 2, [](line& a, line& b) { return a.y == b.y ? a.k > b.k : a.y < b.y; });
        bit.build(1, 1, x.size() - 1, x); bit.change(1, getId(a[0].x1), getId(a[0].x2), 1);
        for (int i = 1; i < n << 1; ++i) {
            ans += bit.tr[1].val * (a[i].y - a[i - 1].y);
            bit.change(1, getId(a[i].x1), getId(a[i].x2), a[i].k);
        }
        cout << "Test case #" << ++cas << "\nTotal explored area: " << setiosflags(ios::fixed) << setprecision(2) << ans << "\n\n";
    }
    return 0;
}

⭐窗内的星星

struct BIT {
    struct Node { long long l, r, mx, val; } tr[80005];
    void push_up(int rt) { tr[rt].mx = max(tr[rt << 1].mx, tr[rt << 1 | 1].mx) + tr[rt].val; }
    void build(int rt, long long l, long long r, vector<long long>& a) {
        tr[rt] = { a[l], a[r], 0, 0 };
        if (l == r) return;
        int mid = l + r >> 1;
        build(rt << 1, l, mid, a); build(rt << 1 | 1, mid + 1, r, a);
    }
    void change(int rt, long long l, long long r, long long k) {
        if (tr[rt].l >= l && tr[rt].r <= r) { tr[rt].mx += k, tr[rt].val += k; return; }
        if (l <= tr[rt << 1].r) change(rt << 1, l, r, k);
        if (r >= tr[rt << 1 | 1].l) change(rt << 1 | 1, l, r, k);
        push_up(rt);
    }
} bit;

int n, W, H, _, cas;
vector<long long> c;

struct line { long long x1, x2, y, k; } a[20000];

int main() {
    while (cin >> n >> W >> H) {
        vector<long long>(1, -1).swap(c); long long ans = 0;
        for (int i = 0; i < n; ++i) {
            long long x, y, z; cin >> x >> y >> z; c.push_back(x); c.push_back(x + W - 1);
            a[i << 1] = { x, x + W - 1, y, z }, a[i << 1 | 1] = { x, x + W - 1, y + H - 1, -z };
        }
        sort(c.begin(), c.end()); c.erase(unique(c.begin(), c.end()), c.end());
        sort(a, a + n * 2, [](line& a, line& b) { return a.y == b.y ? a.k > b.k : a.y < b.y; });
        bit.build(1, 1, c.size() - 1, c);
        for (int i = 0; i < n << 1; ++i) {
            bit.change(1, a[i].x1, a[i].x2, a[i].k);
            ans = max(ans, bit.tr[1].mx);
        }
        cout << ans << '\n';
    }
    return 0;
}

分块

蒲公英

int a[N], idx[N], x, tax[N];
vector<int> c(1, -1), b[N];

void init() {
    sort(c.begin(), c.end()); c.erase(unique(c.begin(), c.end()), c.end());
    sz = pow(m * n * log2(n), 1.0 / 3); len = (n - 1) / sz + 1;
    for(int i=1;i<=n;++i) idx[i]=(i-1)/len+1,b[a[i]=lower_bound(c.begin(),c.end(),a[i])-c.begin()].push_back(i);
    for (int i = 1; i < c.size(); ++i) b[i].push_back(n + 1);
    for (int i = 1, res = a[1]; i <= sz; res = a[i++ * len + 1], memset(tax, 0, sizeof tax))
        for (int j = (i - 1) * len + 1; j <= n; ++j) {
            if (++tax[a[j]] > tax[res]) res = a[j];
            else if (tax[a[j]] == tax[res]) res = min(res, a[j]);
            if (j % len == 0 || j == n) f[i][idx[j]] = res, d[i][idx[j]] = tax[res];
        }
}

int get(int l, int r, int k) { return upper_bound(b[k].begin(), b[k].end(), r) - lower_bound(b[k].begin(), b[k].end(), l); }

long long solve(long long cnt, int l, int r, int L, int R) {
    for (int i = l; i <= r; ++i) if (x != a[i] || i == l) {
        int res = get(L, R, a[i]);
        if (res > cnt) cnt = res, x = a[i];
        else if (res == cnt) x = min(x, a[i]);
    }
    return cnt;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> a[i], c.push_back(a[i]);
    init(); long long ls = 0;
    for (int i = 0; i < m; ++i) {
        int l, r; cin >> l >> r; l = (l + ls - 1) % n + 1, r = (r + ls - 1) % n + 1;
        if (l > r) swap(l, r); x = a[l];
        int L = idx[l] + ((l - 1) % len != 0), R = idx[r] - (r % len && r != n);
        if (L > R) { solve(0, l, r, l, r); cout << (ls = c[x]) << '\n'; continue; }
        x = f[L][R]; L = (L - 1) * len + 1, R = R * len;
        solve(solve(get(l, r, x), l, L - 1, l, r), R + 1, r, l, r);
        cout << (ls = c[x]) << '\n';
    }
    return 0;
}

⭐磁力块

struct node { ll p, m, d, r; } a[250001];

int n, m, sz, len, h[505], mx[505];
bool v[250005];
ll x, y;

long long dist(pair<ll, ll> a, pair<ll, ll> b) {
    return (a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second);
}

int main() {
    cin >> x >> y >> a[0].p >> a[0].r >> n; a[0].r *= a[0].r;
    for (int i = 1; i <= n; ++i) {
        ll c, d; cin >> c >> d >> a[i].m >> a[i].p >> a[i].r;
        a[i].d = dist({ x, y }, { c, d }); a[i].r *= a[i].r;
    }
    sort(a + 1, a + 1 + n, [](node& a, node& b) { return a.m < b.m; });
    sz = sqrt(n); len = (n - 1) / sz + 1;
    for (int i = 1; i <= sz; ++i) {
        mx[i] = a[min(n, i * len)].m, h[i] = (i - 1) * len + 1;
        sort(a + h[i], a + min(i * len, n) + 1, [](node& a, node& b) { return a.d < b.d; });
    }
    stack<int> st; st.push(0);
    while (!st.empty()) {
        int x = st.top(), l = 1; st.pop();
        for (; l <= sz && a[x].p >= mx[l]; ++l) for (; h[l] <= min(l * len, n); ++h[l]) if (!v[h[l]])
            if (a[h[l]].d <= a[x].r) st.push(h[l]), ++m; else break;
        if (l <= sz) for (int i = h[l]; i <= min(l * len, n); ++i) if (!v[i])
            if (a[i].d <= a[x].r && a[i].m <= a[x].p) v[i] = 1, st.push(i), ++m;
    }
    cout << m;
    return 0;
}

小Z的袜子

struct node { int l, r, id, p, f; } q[N];

int t, len, n, m, a[N], b[N], d[N], c[N] = {1};

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= m; ++i) {
        cin >> q[i].l >> q[i].r; q[i].id = i;
        q[i].p = (q[i].r - q[i].l + 1ll) * (q[i].r - q[i].l) >> 1;
    }
    len = sqrt(n); t = (n - 1) / len + 1;
    sort(q + 1, q + 1 + m, [](node& a, node& b) { return a.l < b.l; });
    for (int i = b[1] = d[1] = 1; i <= t && b[i] <= m; ++i, d[i] = b[i] = d[i - 1] + 1) {
        while (d[i] < m && q[d[i] + 1].l <= i * len) ++d[i];
        sort(q + b[i], q + d[i] + 1, [](node& a, node& b) { return a.r < b.r; });
    }
    for (int i = 1, l = 0, r = 0, cur = 0; d[i - 1] < m; ++i) for (int j = b[i]; j <= d[i]; ++j) {
        while (r < q[j].r) { cur += c[a[++r]]; ++c[a[r]]; }
        while (l > q[j].l) { cur += c[a[--l]]; ++c[a[l]]; }
        while (r > q[j].r) { --c[a[r]]; cur -= c[a[r--]]; }
        while (l < q[j].l) { --c[a[l]]; cur -= c[a[l++]]; }
        if (!cur) q[j].p = 1;
        else { q[j].f = __gcd(q[j].p, cur); q[j].p /= q[j].f, q[j].f = cur / q[j].f; }
    }
    sort(q + 1, q + 1 + m, [](node& a, node& b) { return a.id < b.id; });
    for (int i = 1; i <= m; ++i) cout << q[i].f << '/' << q[i].p << '\n';
    return 0;
}

点分治

void add(int u, int v, int c) {
    ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c;
}

void dfsct(int x, int f) {
    siz[x] = 1;
    int mx = 0;
    for (int i = h[x]; i; i = ne[i]) {
        int y = to[i];
        if (v[y] || y == f) continue;
        dfsct(y, x);
        siz[x] += siz[y];
        umax(mx, siz[y]);
    }
    umax(mx, sum - siz[x]);
    if (mx < mxsz) mxsz = mx, root = x;
}

void dfs(int x, int f, int id) {
    q[++tail] = { d[x], b[x] = id };
    for (int i = h[x]; i; i = ne[i]) {
        int y = to[i];
        if (y == f || v[y]) continue;
        d[y] = d[x] + co[i];
        dfs(y, x, id == root ? y : id);
    }
}

void work(int x) {
    dfsct(x, 0); v[x] = 1;
    tail = 0, d[x] = 0; dfs(x, 0, x);
    sort(q + 1, q + 1 + tail);
    for (int l = 1, r = 1, sum; l <= tail; --s[q[++l].se]) {
        while (r < tail && q[l].fi + q[r + 1].fi <= k) ++s[q[++r].se];
        while (r > l && q[l].fi + q[r].fi > k) --s[q[r--].se];
        if (r <= l) { ++s[q[++r].se];  continue; }
        ans += r - l - s[q[l].se];
    }
    for (int i = h[x]; i; i = ne[i]) if (!v[to[i]]) {
        sum = siz[to[i]]; mxsz = n + 1;
        dfsct(to[i], -1); work(root);
    }
}

int main() {
    IOS;
    while (cin >> n >> k, n & k) {
        rep(i, 1, n) h[i] = v[i] = 0; tot = ans = 0;
        rep(i, 2, n) {
            int u, v, c; cin >> u >> v >> c;
            ++u, ++v; add(u, v, c); add(v, u, c);
        }
        sum = n; mxsz = n + 1;
        dfsct(1, -1); work(root);
        cout << ans << '\n';
    }
    return 0;
}

平衡树

普通平衡树

struct FHQ {
    static const int N = 1e5 + 5;
    struct Node {
        int ch[2], val, pri, siz;
        Node (int S = 0, int V = 0, int P = 0, int l = 0, int r = 0) :
            siz(S), val(V), pri(P) { ch[0] = l, ch[1] = r; }
        int& operator [](const int k) { return ch[k]; }
    } tr[N];
    FHQ() { srand((unsigned)time(NULL)); }
    int tot, x, y, z, root;
    int newNode (int v) { tr[++tot] = Node(1, v, rand()); return tot; }
    void update(int x) { tr[x].siz = 1 + tr[tr[x][0]].siz + tr[tr[x][1]].siz; }
    int merge(int x, int y) {
        if (!x || !y) return x + y;
        if (tr[x].pri < tr[y].pri) { tr[x][1] = merge(tr[x][1], y); update(x); return x; }
        else { tr[y][0] = merge(x, tr[y][0]); update(y); return y; }
    }
    void split_v(int p, int k, int &x, int& y) {
        if (!p) x = y = 0;
        else {
            if (tr[p].val <= k) x = p, split_v(tr[p][1], k, tr[p][1], y);
            else y = p, split_v(tr[p][0], k, x, tr[p][0]);
            update(p);
        }
    }
    int kth(int p, int k) {
        while (1) {
            if (k <= tr[tr[p][0]].siz) p = tr[p][0];
            else if (k == tr[tr[p][0]].siz + 1) return p;
            else k -= tr[tr[p][0]].siz + 1, p = tr[p][1];
        }
    }
    int getrk(int val) {
        split_v(root, val - 1, x, y);
        int ans = tr[x].siz + 1;
        return root = merge(x, y), ans;
    }
    void add_v(int pos, int val) {
        split_v(root, pos, x, y);
        root = merge(merge(x, newNode(val)), y);
    }
    void del_v(int val) {
        split_v(root, val, x, z);
        split_v(x, val - 1, x, y);
        y = merge(tr[y][0], tr[y][1]);
        root = merge(merge(x, y), z);
    }
    int pre(int val) {
        split_v(root, val - 1, x, y);
        int ans = tr[kth(x, tr[x].siz)].val;
        return root = merge(x, y), ans;
    }
    int nxt(int val) {
        split_v(root, val, x, y);
        int ans = tr[kth(y, 1)].val;
        return root = merge(x, y), ans;
    }
} T;

const int N = 1e5 + 5;

int n, m, _, k;

int main() {
    IOS; cin >> n;
    for (int i = 1; i <= n; ++i) {
        int op, x; cin >> op >> x;
        switch (op) {
            case 1: T.add_v(x - 1, x); break;
            case 2: T.del_v(x); break;
            case 3: cout << T.getrk(x) << '\n'; break;
            case 4: cout << T.tr[T.kth(T.root, x)].val << '\n'; break;
            case 5: cout << T.pre(x) << '\n'; break;
            case 6: cout << T.nxt(x) << '\n'; break;
            default: break;
        }
    }
    return 0;
}

离线分治算法

天使玩偶

int tot, ans[N << 1], c[M], mxx, mxy;

struct node { int op, x, y, t; } q[N << 1], a[N << 1], b[N << 1];

void add(int x, int k) { for (; x <= mxy; x += -x & x) c[x] = max(c[x], k); }

int ask(int x) { int ans = -1e7; for (; x; x -= -x & x) ans = max(ans, c[x]); return ans; }

void cl(int x) { for (; x <= mxy; x += -x & x) c[x] = -1e7; }

void init(int k) {
    int x = 0, y = 0; tot = 0;
    for (int i = 1; i <= n + m; ++i) {
        if (k == 1) q[i].x = mxx - q[i].x + 1;
        else if (k == 2) q[i].y = mxy - q[i].y + 1;
        if (q[i].op == 2) x = max(x, q[i].x), y = max(y, q[i].y);
    }
    for (int i = 1; i <= n + m; ++i) if (q[i].x <= x && q[i].y <= y) a[++tot] = q[i];
}

void cdq(int l, int r) {
    if (l == r) return;
    int mid = l + r >> 1;
    cdq(l, mid); cdq(mid + 1, r);
    int x = l, y = mid + 1, z = l;
    for (; y <= r; b[z++] = a[y++]) {
        for (; x <= mid && a[x].x <= a[y].x; b[z++] = a[x++])
            if (a[x].op == 1) add(a[x].y, a[x].x + a[x].y);
        if (a[y].op == 2) ans[a[y].t] = min(ans[a[y].t], a[y].y + a[y].x - ask(a[y].y));
    }
    for (int i = l; i < x; ++i) if (a[i].op == 1) cl(a[i].y);
    for (; x <= mid || y <= r;) {
        if (y > r) while (x <= mid) b[z++] = a[x++];
        else if (x > mid) while (y <= r) b[z++] = a[y++];
        else if (a[x].x <= a[y].x) b[z++] = a[x++];
        else b[z++] = a[y++];
    } 
    for (int i = l; i <= r; ++i) a[i] = b[i];
}

int main() {
    cin >> n >> m; vector<int> idx;
    for (int i = 1; i <= n; ++i) {
        cin >> q[i].x >> q[i].y, q[i].t = i; q[i].op = 1;
        mxx = max(mxx, ++q[i].x); mxy = max(mxy, ++q[i].y);
    }
    for (int i = n + 1; i <= n + m; ++i) {
        cin >> q[i].op >> q[i].x >> q[i].y; q[i].t = i;
        mxx = max(mxx, ++q[i].x); mxy = max(mxy, ++q[i].y);
        if (q[i].op == 2) idx.push_back(q[i].t), ans[i] = 1e7;
    }
    for (int i = 1; i <= mxy; ++i) c[i] = -1e7;
    init(0); cdq(1, tot); init(1); cdq(1, tot);
    init(2); cdq(1, tot); init(1); cdq(1, tot);
    for (auto i : idx) cout << ans[i] << '\n';
    return 0;
}

第K个数

struct CRBIT {
    struct node { int l, r, val; } tr[N * 20];
    int rt[N], tot;
    void update(int& x, int y, int l, int r, int d) {
        tr[x = ++tot] = tr[y]; ++tr[x].val;
        if (l == r) return;
        int mid = l + r >> 1;
        if (d <= mid) update(tr[x].l, tr[y].l, l, mid, d);
        if (d > mid) update(tr[x].r, tr[y].r, mid + 1, r, d);
    }
    int ask(int x, int y, int l, int r, int k) {
        if (l == r) return l;
        int val = tr[tr[x].l].val - tr[tr[y].l].val, mid = l + r >> 1;
        if (val < k) return ask(tr[x].r, tr[y].r, mid + 1, r, k - val);
        return ask(tr[x].l, tr[y].l, l, mid, k);
    }
} bit;

int n, m, a[N];

int main() {
    cin >> n >> m; vector<int> c;
    for (int i = 1; i <= n; ++i) cin >> a[i], c.push_back(a[i]);
    sort(c.begin(), c.end()); c.erase(unique(c.begin(), c.end()), c.end());
    for (int i = 1; i <= n; ++i)
        bit.update(bit.rt[i], bit.rt[i - 1], 1, c.size(), lower_bound(c.begin(), c.end(), a[i]) - c.begin() + 1);
    for (int i = 1; i <= m; ++i) {
        int l, r, k; cin >> l >> r >> k;
        cout << c[bit.ask(bit.rt[r], bit.rt[l - 1], 1, c.size(), k) - 1] << '\n';
    }
    return 0;
}

可持久化数据结构

最大异或和

struct SustainableTrie {
    struct node {
        int son[2], cnt;
        int& operator [](const int x) { return son[x]; }
    } tr[N * M << 1];
    int root[N << 1], tot;
    void insert(int& x, int y, int k) {
        int p = x = ++tot; tr[p] = tr[y];
        for (int i = M; ~i; --i) {
            int ch = k >> i & 1;
            tr[p = tr[p][ch] = ++tot] = tr[y = tr[y][ch]];
            ++tr[p].cnt;
        }
    }
    int ask(int x, int y, int k) {
        int ans = 0;
        for (int i = M; ~i; --i) {
            int ch = k >> i & 1;
            if (tr[tr[x][!ch]].cnt - tr[tr[y][!ch]].cnt)
                ans ^= 1 << i, ch = !ch;
            x = tr[x][ch], y = tr[y][ch];
        } return ans;
    }
} trie;

int n, m, _, k, s;

int main() {
    IOS; cin >> n >> m; ++k, trie.insert(trie.root[k], trie.root[0], 0);
    for (int i = 1; i <= n; ++i) cin >> _, ++k, trie.insert(trie.root[k], trie.root[k - 1], s ^= _);
    for (int i = 1; i <= m; ++i) {
        char op[2]; cin >> op;
        if (op[0] == 'A') cin>>_,++k,trie.insert(trie.root[k],trie.root[k - 1],s^=_);
        else {
            int l, r, x; cin >> l >> r >> x;
            cout << trie.ask(trie.root[r], trie.root[l - 1], x ^ s) << '\n';
        }
    } return 0;
}

总结与练习

关押罪犯

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) f[i] = i, f[i + n] = i + n;
    for (int i = 1; i <= m; ++i) cin >> p[i].second.first >> p[i].second.second >> p[i].first;
    sort(p + 1, p + 1 + m);
    for (int i = m; i; --i) {
        int x = p[i].second.first, y = p[i].second.second;
        if (find(x) == find(y)) return cout << p[i].first, 0;
        unit(x, y + n), unit(x + n, y);
    }
    return cout << 0, 0;
}

石头剪子布

int main() {
    while (cin >> n >> m) {
        int ans = n, res = 0, cnt = 0;
        for (int i = 1; i <= m; ++i)  cin >> a[i] >> c[i] >> b[i];
        for (int k = 0; k < n; ++k) {
            for (int i = 0; i < n; ++i) f[i] = i, f[i + n] = i + n, f[i + n * 2] = i + n * 2;
            for (int i = 1; i <= m; ++i) {
                if (a[i] == k || b[i] == k) continue;
                if (c[i] == '=')
                    if (find(a[i]) == find(b[i] + n) || find(a[i]) == find(b[i] + n * 2)) {
                        --ans, res = max(res, i);
                        break;
                    }
                    else unit(a[i], b[i]), unit(a[i] + n, b[i] + n), unit(a[i] + n * 2, b[i] + n * 2);
                else if (c[i] == '>')
                    if (find(a[i]) == find(b[i]) || find(a[i]) == find(b[i] + n)) {
                        --ans, res = max(res, i);
                        break;
                    }
                    else unit(a[i], b[i] + n * 2), unit(a[i] + n, b[i]), unit(a[i] + n * 2, b[i] + n);
                else
                    if (find(a[i]) == find(b[i]) || find(a[i]) == find(b[i] + n * 2)) {
                        --ans, res = max(res, i);
                        break;
                    }
                    else unit(a[i], b[i] + n), unit(a[i] + n, b[i] + n * 2), unit(a[i] + n * 2, b[i]);
                if (i == m) cnt = k;
            }
        }
        if (!ans) cout << "Impossible\n";
        else if (ans != 1) cout << "Can not determine\n";
        else cout << "Player " << cnt << " can be determined to be the judge after " << res << " lines\n";
    }
    return 0;
}

真正的骗子

int main() {
    while (cin >> n >> p1 >> p2, n || p1 || p2) {
        bool g = 0;
        for (int i = 1; i <= p1 + p2; ++i) f[i] = i, f[i + p1 + p2] = i + p1 + p2, sz[i] = 1, sz[i + p1 + p2] = 0;
        for (int i = 0; i < n; ++i) {
            int x, y; char s[5]; cin >> x >> y >> s;
            if (s[0] == 'n')
                if (find(x) == find(y)) g = 1;
                else unit(x, y + p1 + p2), unit(x + p1 + p2, y);
            else
                if (find(x) == find(y + p1 + p2)) g = 1;
                else unit(x, y), unit(x + p1 + p2, y + p1 + p2);
        }
        vector<pair<int, pair<int, int>>> a(1); vector<unordered_set<int>> st(1); st[0].insert(0);
        for (int i = 1; i <= p1 + p2; ++i) if (f[i] == i) {
            a.push_back({ i, {sz[i], sz[find(i + p1 + p2)]} }); st.push_back(unordered_set<int>());
            for (auto b : st[st.size() - 2]) st.back().insert(b + sz[i]), st.back().insert(b + sz[find(i + p1 + p2)]);
        }
        unordered_set<int> ans;
        for (int i = a.size() - 1, res = p1; i; --i)
            if (st[i - 1].count(res - a[i].second.first) && st[i - 1].count(res - a[i].second.second)) { g = 1; break; }
            else if (st[i - 1].count(res - a[i].second.first)) ans.insert(a[i].first), res -= a[i].second.first;
            else ans.insert(a[i].first + p1 + p2), res -= a[i].second.second;
        if (!st.back().count(p1) || g) { cout << "no\n"; continue; }
        for (int i = 1; i <= p1 + p2; ++i) if (ans.count(find(i))) cout << i << '\n'; cout << "end\n";
    }
    return 0;
}

买票

void add(int x, int k) { for (; x <= n; x += -x & x) c[x] += k;  }

int ask(int x) { int ans = 0; for (; x; x -= -x & x) ans += c[x]; return ans; }

int main () {
    while (cin >> n) {
        memset(c, 0, sizeof c);
        for (int i = 1; i <= n; ++i) cin >> a[i].first >> a[i].second, ++a[i].first, add(i, 1);
        for (int i = n, l = 1, r = n; i; --i, l = 1, r = n) {
            while (l < r) {
                int mid = l + r >> 1;
                if (ask(mid) >= a[i].first) r = mid;
                else l = mid + 1;
            }
            rk[l] = a[i].second; add(l, -1);
        }
        for (int i = 1; i <= n; ++i) cout << rk[i] << ' '; cout << '\n';
    }
    return 0;
}

旅馆

struct BIT {
    static const int N = 5e4 + 5;
    struct node { int val[4], l, r, len, tag; } tr[N << 2];
    void push_up(int rt) {
        tr[rt].val[0] = tr[rt << 1].val[0] + (tr[rt << 1].val[0] ^ tr[rt << 1].len ? 0 : tr[rt << 1 | 1].val[0]);
        tr[rt].val[1] = tr[rt << 1 | 1].val[1] + (tr[rt << 1 | 1].val[1] ^ tr[rt << 1 | 1].len ? 0 : tr[rt << 1].val[1]);
        if (tr[rt << 1].val[1] && tr[rt << 1 | 1].val[0]) tr[rt].val[2] = tr[rt << 1].val[1] + tr[rt << 1 | 1].val[0];
        else tr[rt].val[2] = 0;
        tr[rt].val[3] = max(max(tr[rt << 1].val[3], tr[rt << 1 | 1].val[3]), tr[rt].val[2]);
    }
    void push_down(int rt) {
        if (!tr[rt].tag) return;
        if (tr[rt].tag == -1) for (int i = 0; i < 4; ++i) tr[rt << 1].val[i] = tr[rt << 1 | 1].val[i] = 0;
        else for (int i = 0; i < 4; ++i) tr[rt << 1].val[i] = tr[rt << 1].len, tr[rt << 1 | 1].val[i] = tr[rt << 1 | 1].len;
        tr[rt << 1 | 1].tag = tr[rt << 1].tag = tr[rt].tag; tr[rt].tag = 0;
    }
    void build(int rt, int l, int r) {
        tr[rt].l = l, tr[rt].r = r, tr[rt].len = r - l + 1;
        if (l == r) { tr[rt].val[0] = tr[rt].val[1] = tr[rt].val[2] = tr[rt].val[3] = 1; return; }
        int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r);
        push_up(rt);
    }
    void change(int rt, int l, int r, int k) {
        if (tr[rt].l >= l && tr[rt].r <= r) {
            if (k == -1) for (int i = 0; i < 4; ++i) tr[rt].val[i] = 0;
            else for (int i = 0; i < 4; ++i) tr[rt].val[i] = tr[rt].len;
            tr[rt].tag = k; return;
        }
        push_down(rt);
        int mid = tr[rt].l + tr[rt].r >> 1;
        if (l <= mid) change(rt << 1, l, r, k);
        if (r > mid) change(rt << 1 | 1, l, r, k);
        push_up(rt);
    }
    int ask(int rt, int len) {
        if (tr[rt].val[3] < len) return 0;
        if (tr[rt].val[0] >= len) return tr[rt].l;
        push_down(rt); int ans = 0;
        if (tr[rt << 1].val[3] >= len) ans = ask(rt << 1, len);
        else if (tr[rt].val[2] >= len) ans = tr[rt << 1].r - tr[rt << 1].val[1] + 1;
        else ans = ask(rt << 1 | 1, len);
        push_up(rt); return ans;
    }
} bit;

int n, m;

int main() {
    cin >> n >> m; bit.build(1, 1, n);
    for (int i = 0; i < m; ++i) {
        int op, x, y; cin >> op >> x;
        if (op == 1) {
            int ans = bit.ask(1, x); cout << ans << '\n';
            if (ans) bit.change(1, ans, ans + x - 1, -1);
        }
        else cin >> y, bit.change(1, x, y + x - 1, 1);
    }
    return 0;
}

海报

struct BIT {
    static const int N = 1e4 + 5;
    struct node { int l, r, len, cnt, tag, val[2]; } tr[N << 2];
    void push(int rt) {
        if (tr[rt].tag) tr[rt].val[0] = tr[rt].val[1] = tr[rt].len, tr[rt].cnt = 1;
        else if (tr[rt].l + 1 == tr[rt].r) tr[rt].val[0] = tr[rt].val[1] = 0, tr[rt].cnt = 0;
        else {
            tr[rt].cnt = tr[rt << 1].cnt + tr[rt << 1 | 1].cnt - (tr[rt << 1].val[1] && tr[rt << 1 | 1].val[0] ? 1 : 0);
            tr[rt].val[0] = tr[rt << 1].val[0] + (tr[rt << 1].val[0] ^ tr[rt << 1].len ? 0 : tr[rt << 1 | 1].val[0]);
            tr[rt].val[1] = tr[rt << 1 | 1].val[1] + (tr[rt << 1 | 1].val[1] ^ tr[rt << 1 | 1].len ? 0 : tr[rt << 1].val[1]);
        }
    }
    void build(int rt, int l, int r, vector<int>& a) {
        tr[rt].l = l, tr[rt].r = r, tr[rt].len = a[r] - a[l];
        if (l + 1 == r) return;
        int mid = l + r >> 1; build(rt << 1, l, mid, a); build(rt << 1 | 1, mid, r, a);
    }
    void change(int rt, int l, int r, int k) {
        if (tr[rt].l >= l && tr[rt].r <= r) { tr[rt].tag += k; push(rt); return; }
        int mid = tr[rt].l + tr[rt].r >> 1;
        if (l < mid) change(rt << 1, l, r, k);
        if (r > mid) change(rt << 1 | 1, l, r, k);
        push(rt);
    }
} bit;

struct line { int y, a, b, k; } a[10005];

int n, x[10000], y[10000];

long long work(int x[], int y[]) {
    vector<int> c; c.push_back(-2e9);
    for (int i = 0; i < n; ++i) {
        a[i << 1] = { y[i << 1], x[i << 1], x[i << 1 | 1], 1 };
        a[i << 1 | 1] = { y[i << 1 | 1], x[i << 1], x[i << 1 | 1], -1 };
        c.push_back(x[i << 1]); c.push_back(x[i << 1 | 1]);
    }
    sort(c.begin(), c.end()); c.erase(unique(c.begin(), c.end()), c.end());
    sort(a, a + n * 2, [](line& a, line& b) { return a.y ^ b.y ? a.y < b.y : a.k > b.k; });
    long long ans = 0; bit.build(1, 1, c.size() - 1, c);
    bit.change(1, lower_bound(c.begin(), c.end(), a[0].a) - c.begin(), lower_bound(c.begin(), c.end(), a[0].b) - c.begin(), a[0].k);
    for (int i = 1; i < n << 1; ++i) {
        ans += (long long)bit.tr[1].cnt * (a[i].y - a[i - 1].y) << 1;
        bit.change(1, lower_bound(c.begin(), c.end(), a[i].a) - c.begin(), lower_bound(c.begin(), c.end(), a[i].b) - c.begin(), a[i].k);
    }
    return ans;
}

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) cin >> x[i << 1] >> y[i << 1] >> x[i << 1 | 1] >> y[i << 1 | 1];
    cout << work(x, y) + work(y, x);
    return 0;
}

作诗

int n, m, k, cnt[M][N], val[M][M], t, len, a[N], id[N];
vector<int> c;

void init() {
    for (int i = 1; i <= n; ++i) cin >> a[i], c.push_back(a[i]);
    sort(all(c)); c.erase(unique(all(c)), c.end());
    t = pow(n, 1.0 / 3); len = (n - 1) / t + 1; t = (n - 1) / len + 1;
    for (int i = 1; i <= n; ++i) a[i] = lower_bound(all(c), a[i]) - c.begin(), id[i] = (i - 1) / len + 1;
    for (int i = t, res = 0; i; --i, res = 0) {
        for (int j = min(n, i * len); j; --j) {
            ++cnt[i][a[j]];
            if ((cnt[i][a[j]] & 1) && cnt[i][a[j]] - 1) --res;
            else if (cnt[i][a[j]] - 1) ++res;
            if (j % len == 1) val[id[j]][i] = res;
        }
    }
}

int work(int l, int L, int R, int r) {
    int ans = val[id[L]][id[R]];
    for (int i = l; i < L; ++i) {
        ++cnt[id[L] - 1][a[i]];
        if (cnt[id[R]][a[i]] - cnt[id[L] - 1][a[i]] & 1) --ans;
        else if (cnt[id[R]][a[i]] - cnt[id[L] - 1][a[i]]) ++ans;
    }
    for (int i = r; i > R; --i) {
        --cnt[id[R]][a[i]];
        if (cnt[id[R]][a[i]] - cnt[id[L] - 1][a[i]] & 1) --ans;
        else if (cnt[id[R]][a[i]] - cnt[id[L] - 1][a[i]]) ++ans;
    }
    for (int i = l; i < L; ++i) --cnt[id[L] - 1][a[i]];
    for (int i = r; i > R; --i) ++cnt[id[R]][a[i]];
    return ans;
}

int main() {
    IOS; cin >> n >> k >> m; init();
    for (int i = 0, ls = 0; i < m; ++i) {
        int l, r; cin >> l >> r; l = (l + ls) % n + 1, r = (r + ls) % n + 1;
        if (l > r) swap(l, r);
        cout << (ls = work((id[l] - 1) * len + 1, l, r, min(id[r] * len, n))) << '\n';
    }
    return 0;
}

权值

int n, k, gra, sz[N], mxsz, q[N], f[N], ans;
bool v[N];
pair<int, int> dis[N];
vector<pair<int, int>> h[N];

void gravity(int x, int fa, int s) {
    sz[x] = 1; int mx = 0;
    for (auto& y : h[x]) if (y.first != fa && !v[y.first]) {
        gravity(y.first, x, s); sz[x] += sz[y.first];
        mx = max(mx, sz[y.first]);
    }
    if ((mx = max(mx, s - sz[x])) < mxsz) gra = x, mxsz = mx;
}

void dfs(int x, int fa, int id, int& tail) {
    for (auto& y : h[x]) if (y.first != fa && !v[y.first]) {
        f[y.first] = id; dis[q[++tail] = y.first].second = dis[x].second + 1;
        dis[y.first].first = dis[x].first + y.second;
        if (dis[y.first].first < k) dfs(y.first, x, id, tail);
    }
}

void work(int x) {
    int tail = 0; dis[x] = { 0, 0 }; q[++tail] = x; v[f[x] = x] = 1;
    /*for (auto& y : h[x]) if (!v[y.first] && y.second <= k)
        dis[q[++tail] = y.first] = { y.second, 1 }, dfs(y.first, x, f[y.first] = y.first, tail);
    sort(q + 1, q + tail + 1, [&](int a, int b) { return dis[a] < dis[b]; });
    for (int l = 1, r = 1; l <= tail; ++l) {
        while (l < r - 1 && dis[q[l]].first + dis[q[r - 1]].first >= k) --r;
        while (r < tail && dis[q[l]].first + dis[q[r]].first < k) ++r;
        for (; r <= tail && dis[q[l]].first + dis[q[r]].first == k; ++r)
            if (f[q[l]] ^ f[q[r]]) ans = min(ans, dis[q[l]].second + dis[q[r]].second);
    }*/
    unordered_map<int, int> st; st[0] = 0;//st可换成桶,循环完把更新过的位置重新设max即可
    for (auto& y : h[x]) if (!v[y.first] && y.second <= k) {
        dis[q[++tail] = y.first] = { y.second, 1 }; q[tail = 1] = y.first;
        dfs(y.first, x, f[y.first] = y.first, tail);
        for (int i = 1; i <= tail; ++i) if (st.count(k - dis[q[i]].first))
            ans = min(ans, st[k - dis[q[i]].first] + dis[q[i]].second);
        for (int i = 1; i <= tail; ++i) {
            auto it = st.find(dis[q[i]].first);
            if (it == st.end()) st.insert(dis[q[i]]);
            else it->second = min(it->second, dis[q[i]].second);
        }
    }

    gravity(x, -1, n); mxsz = n;
    for (auto& y : h[x]) if (!v[y.first]) gravity(y.first, -1, sz[y.first]), work(gra), mxsz = n;
}

int main() {
    IOS; cin >> n >> k; mxsz = ans = n;
    for (int i = 1; i < n; ++i) {
        int u, v, c; cin >> u >> v >> c;
        h[u].push_back({ v, c }); h[v].push_back({ u, c });
    }
    gravity(0, -1, n); work(gra); cout << (ans ^ n ? ans : -1);
    return 0;
}

营业额统计

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(0); cin.tie(0);
    long long ans;
    int n; cin >> n >> ans;
    set<int> st; st.insert(ans);
    st.insert(1e9); st.insert(-(1e9));
    for (int i = 2; i<= n; ++i) {
        int x; cin >> x;
        auto it = st.lower_bound(x);
        ans += min(abs(x - *(--it)), abs(x - *it));
        st.insert(x);
    }
    cout << ans;
    return 0;
}

超级备忘录

struct FHQ {
    static const int N = 5e5 + 5;

    struct Node {
        int ch[2], pri, siz;
        ll tag, val, miv;
        bool rever;
        int& operator [](const int k) { return ch[k]; }
    } tr[N];

    FHQ () { srand((unsigned)time(NULL)); }

    int tot, x, y, z, root;

    int newNode (int val) {
        tr[++tot].val = val; tr[tot].pri = rand();
        tr[tot].miv = val; tr[tot].siz = 1; 
        return tot;
    }

    void push_up(int p) {
        tr[p].siz = 1 + tr[tr[p][0]].siz + tr[tr[p][1]].siz;
        tr[p].miv = tr[p].val;
        if (tr[p][0]) umin(tr[p].miv, tr[tr[p][0]].miv);
        if (tr[p][1]) umin(tr[p].miv, tr[tr[p][1]].miv);
    }

    void down_tag(int p) {
        ll tag = tr[p].tag; tr[p].tag = 0;
        tr[p].val += tag; tr[p].miv += tag;
        if (tr[p][0]) tr[tr[p][0]].tag += tag;
        if (tr[p][1]) tr[tr[p][1]].tag += tag;
    }

    void down_rever(int p) {
        tr[p].rever = 0;
        swap(tr[p][1], tr[p][0]);
        if (tr[p][0]) tr[tr[p][0]].rever ^= 1;
        if (tr[p][1]) tr[tr[p][1]].rever ^= 1;
    }

    void push_down(int p) {
        if (tr[tr[p][0]].tag) down_tag(tr[p][0]);
        if (tr[tr[p][1]].tag) down_tag(tr[p][1]);
        if (tr[p].rever) down_rever(p);
    }

    int merge(int x, int y) {
        if (!x || !y) return x + y;
        push_down(x); push_down(y);
        if (tr[x].pri < tr[y].pri) tr[x][1] = merge(tr[x][1], y);
        else tr[y][0] = merge(x, tr[y][0]), swap(x, y);
        push_up(x); return x;
    }

    void split(int p, int k, int &x, int &y) {
        if (!p) x = y = 0;
        else {
            push_down(p);
            if (k <= tr[tr[p][0]].siz) y = p, split(tr[p][0], k, x, tr[p][0]);
            else x = p, split(tr[p][1], k - tr[tr[p][0]].siz - 1, tr[p][1], y);
            push_up(p);
        }
    }

    void add(int pos, int val) {
        split(root, pos, x, y);
        root = merge(merge(x, newNode(val)), y);
    }

    void del(int k) {
        split(root, k, x, z);
        split(x, k - 1, x, y);
        root = merge(x, z);
    }

    void res(int l, int r) { //反转l, r
        split(root, r, x, z); split(x, l - 1, x, y);
        tr[y].rever ^= 1;
        root = merge(merge(x, y), z);
    }

    void rev(int l, int r, ll t) {
        t %= (r - l + 1);
        if (!t) return;
        split(root, r, root, z);
        split(root, r - t, root, y);
        split(root, l - 1, root, x);
        root = merge(merge(root, y), merge(x, z));
    }

    void change(int l, int r, ll k) {
        split(root, r, x, z); split(x, l - 1, x, y);
        tr[y].tag += k; down_tag(y);
        root = merge(merge(x, y), z);
    }

    ll minval(int l, int r) {
        split(root, r, x, z); split(x, l - 1, x, y);
        ll ans = tr[y].miv;
        root = merge(merge(x, y), z);
        return ans;
    }
} T;

const int N = 1e5 + 5;

int n, m, _, k;
ll a[N];

int main() {
    IOS; cin >> n;
    rep (i, 1, n) cin >> m, T.add(i - 1, m);
    for (cin >> _; _; --_) {
        string op; cin >> op;
        if (op[0] == 'A') {
            int l, r; ll d; cin >> l >> r >> d;
            T.change(l, r, d);
        } else if (op[0] == 'R' && op[3] == 'E') {
            int l, r; cin >> l >> r;
            T.res(l, r);
        } else if (op[0] == 'R') {
            int l, r; ll t; cin >> l >> r >> t;
            T.rev(l, r, t);
        } else if (op[0] == 'I') {
            int val, key; cin >> val >> key;
            T.add(val, key);
        } else if (op[0] == 'D') {
            int x; cin >> x;
            T.del(x);
        } else {
            int l, r; cin >> l >> r;
            cout << T.minval(l, r) << '\n';
        }
    }
    return 0;
}

莫基亚

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 2e6 + 5, M = 1.8e5 + 5;

struct Query { int y, l, r, t, op; } a[M], b[M];

int W;
ll c[N], ans[M], tot;

void add(int x, int k) { for (; x <= W; x += -x & x) c[x] += k; }

ll ask(int x) { ll ans = 0; for (; x; x -= -x & x) ans += c[x]; return ans; }

void cdq(int l, int r) {
    if (l == r) return;
    int mid = l + r >> 1; cdq(l, mid); cdq(mid + 1, r);
    int x = l, y = mid + 1, z = l;
    for (; y <= r; b[z++] = a[y++]) {
        for (; x <= mid && a[x].y <= a[y].y; b[z++] = a[x++])
            if (a[x].op == 1) add(a[x].l, a[x].r);
        if (a[y].op == 2) ans[a[y].t] += ask(a[y].r) - ask(a[y].l - 1);
    }
    for (int i = l; i < x; ++i) if (a[i].op == 1) add(a[i].l, -a[i].r);
    for (int i = x; i <= mid; ++i) b[z++] = a[i];
    for (int i = l; i <= r; ++i) a[i] = b[i];
}

int main() {
    cin >> W >> W; vector<int> id;
    while (cin >> a[++tot].op, a[tot].op != 3) {
        if (a[tot].op == 1) cin >> a[tot].l >> a[tot].y >> a[tot].r, a[tot].t = tot;
        else {
            cin >> a[tot].l >> a[tot].y >> a[tot].r; --a[tot].y; cin >> a[++tot].y;
            a[tot].l = a[tot - 1].l; a[tot].r = a[tot - 1].r; a[tot].op = 2;
            a[tot - 1].t = tot - 1; a[tot].t = tot; id.push_back(tot);
        }
    }
    cdq(1, tot - 1); for (auto& i : id) cout << ans[i] - ans[i - 1] << '\n';
    return 0;
}

流星

struct node { ll op, l, r, id; } q[N << 1], qu[N << 1];

int n, m, _, k;
ll ans[N], c[2][N];
vector<int> a[N];

void add(int x, ll k) { for (int i = x; i <= m; i += -i & i) c[0][i] += k, c[1][i] += x * k; }

void add(int l, int r, ll k) { add(l, k); add(r + 1, -k); }

ll ask(int x) {
    ll p = 0, q = 0, f = x + 1;
    for (; x; x -= -x & x) p += c[0][x], q += c[1][x];
    return p * f - q;
}

ll ask(int l, int r) { return ask(r) - ask(l - 1); }

void solve(int l, int r, int ql, int qr) {
    if (ql > qr) return;
    if (l == r) {
        rep(i, ql, qr) if (q[i].op == 0) ans[q[i].id] = l;
        return;
    }
    int mid = l + r >> 1, nl = ql - 1, nr = 0;
    rep(i, ql, qr)
        if (q[i].op) {
            if (q[i].id > mid) { qu[++nr] = q[i]; continue; }
            if (q[i].l <= q[i].r) add(q[i].l, q[i].r, q[i].op);
            else add(q[i].l, m, q[i].op), add(1, q[i].r, q[i].op);
            q[++nl] = q[i];
        }
        else {
            __int128 cur = 0;//3e5*3e5*1e9 爆ll
            for (auto j : a[q[i].id]) cur += ask(j, j);
            if (cur >= q[i].l) q[++nl] = q[i];
            else q[i].l -= cur, qu[++nr] = q[i];
        }

    rep(i, ql, nl) if (q[i].op)
        if (q[i].l <= q[i].r) add(q[i].l, q[i].r, -q[i].op);
        else add(q[i].l, m, -q[i].op), add(1, q[i].r, -q[i].op);

    rep(i, 1, nr) q[i + nl] = qu[i];
    solve(l, mid, ql, nl); 
    solve(mid + 1, r, nl + 1, qr);
}

int main() {
    cin >> n >> m;
    rep(i, 1, m) cin >> _, a[_].push_back(i);
    rep(i, 1, n) cin >> qu[i].l, qu[i].op = 0, qu[i].id = i;
    cin >> k;
    rep(i, 1, k) cin >> q[i].l >> q[i].r >> q[i].op, q[i].id = i;
    ++k; q[k] = { (int)2e9, 1, m, k };
    rep(i, 1, n) q[k + i] = qu[i];
    solve(1, k, 1, k + n);
    rep(i, 1, n) if (ans[i] != k) cout << ans[i] << '\n'; else cout << "NIE\n";
    return 0;
}

Fotile模拟赛L

struct SustainableTrie {
    struct node {
        int son[2], cnt;
        int& operator [](const int x) { return son[x]; }
    } tr[N * M];
    int rt[N], tot;
    void init() {
        for (int i = 1; i <= tot; ++i) rt[i] = 0;
        tot = 0;
        for (int i = 0; i < M; ++i) tr[0][i] = 0;
    }
    void insert(int& x, int y, int k) {
        int p = x = ++tot; tr[p] = tr[y];
        for (int i = M - 1; ~i; --i) {
            int ch = k >> i & 1;
            tr[p = tr[p][ch] = ++tot] = tr[y = tr[y][ch]];
            ++tr[p].cnt;
        }
    }
    int ask(int x, int y, int k) {
        int ans = 0;
        for (int i = M - 1; ~i; --i) {
            int ch = k >> i & 1;
            if (tr[tr[x][!ch]].cnt - tr[tr[y][!ch]].cnt)
                ans ^= 1ll << i, ch = !ch;
            x = tr[x][ch], y = tr[y][ch];
        } return ans;
    }
} T, RT;

int n, m, f[151][151], t, len, a[N], b[N], id[N];

void init() {
    len = 150; t = (n - 1) / len + 1;
    for (int i = 1; i <= n; ++i) cin >> a[i], id[i] = (i - 1) / len + 1;
    T.insert(T.rt[1], T.rt[0], 0); RT.insert(RT.rt[1], RT.rt[0], 0);
    for (int i = 1; i <= n; ++i) b[i] = a[n + 1 - i], RT.insert(RT.rt[i + 1], RT.rt[i], b[i] ^= b[i - 1]);
    for (int i = 1; i <= n; ++i) T.insert(T.rt[i + 1], T.rt[i], a[i] ^= a[i - 1]);
    for (int i = 1, res = 0; i <= t; ++i, res = 0) for (int j = (i - 1) * len + 1; j <= n; ++j) {
        res = max(res, T.ask(T.rt[j], T.rt[(i - 1) * len], a[j]));
        if (j % len == 0 || j == n) f[i][id[j]] = res;
    }
}

int ask(SustainableTrie& T, int l, int r, int a[], int L) {
    int ans = 0;
    for (int i = l; i <= r; ++i)
        ans = max(ans, T.ask(T.rt[i], T.rt[L - 1], a[i]));
    return ans;
}

int main() {
    IOS; cin >> n >> m; init();
    for (int i = 0, ls = 0; i < m; ++i) {
        int l, r; cin >> l >> r;
        l = ((long long)l + ls) % n + 1, r = ((long long)r + ls) % n + 1;
        if (l > r) swap(l, r);
        if (id[r] - id[l] < 2) cout << (ls = ask(T, l, r, a, l)) << '\n';
        else {
            ls = f[id[l] + 1][id[r] - 1];
            ls = max(ls, max(ask(T, (id[r] - 1) * len + 1, r, a, l),
                ask(RT, (id[n + 1 - l] - 1) * len + 1, n + 1 - l, b, n + 1 - r)));
            cout << ls << '\n';
        }
    }
    return 0;
}

可持久化并查集加强版

动态规划

线性DP

杨老师的照相排列

int n, m, _, k, cas, s[6];
ll f[31][16][11][8][7];

int main() {
    IOS; f[0][0][0][0][0] = 1;
    while (cin >> n, n) {
        rep (i, 1, n) cin >> s[i];
        rep (i, n + 1, 5) s[i] = 0;
        rep (a, 1, s[1]) rep (b, 0, min(a, s[2])) rep (c, 0, min(b, s[3]))
            rep (d, 0, min(c, s[4])) rep (e, 0, min(d, s[5])) {
                f[a][b][c][d][e] = 0;
                if (a) f[a][b][c][d][e] += f[a - 1][b][c][d][e];
                if (b) f[a][b][c][d][e] += f[a][b - 1][c][d][e];
                if (c) f[a][b][c][d][e] += f[a][b][c - 1][d][e];
                if (d) f[a][b][c][d][e] += f[a][b][c][d - 1][e];
                if (e) f[a][b][c][d][e] += f[a][b][c][d][e - 1];
            }
        cout << f[s[1]][s[2]][s[3]][s[4]][s[5]] << '\n';
    }
    return 0;
}

⭐最长上升公共子序列

最长上升子序列多了个公共

即当且仅当 a[i] == b[j] 的时候, b[j] 才能去拼接到已有的子序列上

注意已有的子序列也是不断通过(a[ii < i] == b[jj < j], a[i] < a[i])更得到的, 故已有的子序列就是公共的子序列

int n, m, f[N], a[N];

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1, mx = 0; i <= n; ++i, mx = 0) {
        cin >> m;
        for (int j = 1; j <= n; ++j)
            if (a[j] == m) f[j] = max(mx + 1, f[j]);
            else if (m > a[j]) mx = max(mx, f[j]);
    }
    cout << *max_element(f + 1, f + 1 + n);
    return 0;
}

⭐分级

注意观察决策集合的范围随状态的变化情况, 决策元素只增不减(等),就可以用一个变量优化掉一个循环

long long work(int f) {
    long long ans = 0;
    priority_queue<int> q;
    for (int i = 0; i < n; ++i) {
        q.push(a[i] * f);
        if (a[i] * f < q.top()) {
            ans += q.top() - a[i] * f;
            q.pop(); q.push(a[i] * f);
        }
    }
    return ans;
}

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) cin >> a[i];
    cout << min(work(1), work(-1));
    return 0;
}

⭐移动服务

int n, m, _, k = 2e9, cas;
int d[N][N], a[2], f[2][N][N];

int main() {
    IOS; cin >> n >> m; memset(f[0], 0x3f, sizeof f[0]);
    rep (i, 1, n) rep (j, 1, n) cin >> d[i][j];
    a[f[0][1][2] = 0] = 3;
    rep (i, 1, m) {
        cin >> a[i & 1]; memset(f[i & 1], 0x3f, sizeof f[0]);
        rep (x, 1, n) rep (y, 1, n) if ((x ^ y) && (x ^ a[i & 1 ^ 1]) && (y ^ a[i & 1 ^ 1])) {
            if ((x ^ a[i]) && (a[i & 1 ^ 1] ^ a[i]))
                f[i & 1][a[i & 1 ^ 1]][x] = f[i & 1][x][a[i & 1 ^ 1]] =
                    min(f[i & 1][a[i & 1 ^ 1]][x], f[i & 1 ^ 1][x][y] + d[y][a[i & 1]]);
            if ((y ^ a[i]) && (a[i & 1 ^ 1] ^ a[i]))
                f[i & 1][a[i & 1 ^ 1]][y] = f[i & 1][y][a[i & 1 ^ 1]] =
                    min(f[i & 1][a[i & 1 ^ 1]][y], f[i & 1 ^ 1][x][y] + d[x][a[i & 1]]);
            if ((y ^ a[i]) && (x ^ a[i]))
                f[i & 1][x][y] = f[i & 1][y][x] = 
                    min(f[i & 1][x][y], f[i & 1 ^ 1][x][y] + d[a[i & 1 ^ 1]][a[i & 1]]);
        }
    }
    rep (i, 1, n) rep (j, 1, n) umin(k, f[n & 1][i][j]);
    cout << k;
    return 0;
}

传纸条

int main() {
    cin >> m >> n;
    for (int i = 1; i <= m; ++ i) for(int j = 1; j <= n; ++ j) cin >> mp[i][j];
    memset(f, -1, sizeof f);
    f[2][1][1] = 0;
    for (int k = 3; k < m + n; ++ k) for(int i = 1; i < n; ++ i) for(int j = i + 1; j <= n; ++ j) {
        int s = max(max(f[k - 1][i][j], f[k - 1][i - 1][j]), max(f[k - 1][i][j - 1], f[k - 1][i - 1][j - 1]));
        if (s ^ -1) f[k][i][j] = s + mp[k - i][i] + mp[k - j][j];
    }
    cout << f[m + n - 1][n - 1][n];
    return 0;
 } 

区域

struct path { int k, i, j, l, r; } p[N * N][N][2][2][N][N];

int n, m, k, sum[N][N], f[N * N][N][2][2][N][N], ans, w;

int main() {
    IOS; cin >> n >> m >> k; memset(f, -1, sizeof f); memset(f[0], 0, sizeof f[0]);
    rep(i, 1, n) rep(j, 1, m) cin >> sum[i][j], sum[i][j] += sum[i][j - 1];
    rep(i, 1, n) rep(j, 1, k) rep(l, 1, m) rep(r, l, m) if (j >= r - l + 1) {
        int len = r - l + 1, s = sum[i][r] - sum[i][l - 1];
        rep(a, l, r) rep(b, a, r)
            if (~f[j - len][i - 1][1][1][a][b] && umax(f[j][i][1][1][l][r], f[j - len][i - 1][1][1][a][b] + s))
                p[j][i][1][1][l][r] = { j - len, 1, 1, a, b };
        rep(a, l, r) rep(b, r, m) {
            if (~f[j - len][i - 1][1][1][a][b] && umax(f[j][i][1][0][l][r], f[j - len][i - 1][1][1][a][b] + s))
                p[j][i][1][0][l][r] = { j - len, 1, 1, a, b };
            if (~f[j - len][i - 1][1][0][a][b] && umax(f[j][i][1][0][l][r], f[j - len][i - 1][1][0][a][b] + s))
                p[j][i][1][0][l][r] = { j - len, 1, 0, a, b };
        }
        rep(a, 1, l) rep(b, l, r) {
            if (~f[j - len][i - 1][1][1][a][b] && umax(f[j][i][0][1][l][r], f[j - len][i - 1][1][1][a][b] + s))
                p[j][i][0][1][l][r] = { j - len, 1, 1, a, b };
            if (~f[j - len][i - 1][0][1][a][b] && umax(f[j][i][0][1][l][r], f[j - len][i - 1][0][1][a][b] + s))
                p[j][i][0][1][l][r] = { j - len, 0, 1, a, b };
        }
        rep(a, 1, l) rep(b, r, m) {
            if (~f[j - len][i - 1][1][1][a][b] && umax(f[j][i][0][0][l][r], f[j - len][i - 1][1][1][a][b] + s))
                p[j][i][0][0][l][r] = { j - len, 1, 1, a, b };
            if (~f[j - len][i - 1][1][0][a][b] && umax(f[j][i][0][0][l][r], f[j - len][i - 1][1][0][a][b] + s))
                p[j][i][0][0][l][r] = { j - len, 1, 0, a, b }; 
            if (~f[j - len][i - 1][0][1][a][b] && umax(f[j][i][0][0][l][r], f[j - len][i - 1][0][1][a][b] + s))
                p[j][i][0][0][l][r] = { j - len, 0, 1, a, b }; 
            if (~f[j - len][i - 1][0][0][a][b] && umax(f[j][i][0][0][l][r], f[j - len][i - 1][0][0][a][b] + s))
                p[j][i][0][0][l][r] = { j - len, 0, 0, a, b }; 
        }
    }
    rep(i, 1, n) rep(l, 1, m) rep(r, l, m) rep (x, 0, 1) rep (y, 0, 1)
        if (umax(ans, f[k][i][x][y][l][r])) p[0][0][0][0][0][0] = { k, x, y, l, r }, w = i;
    cout << "Oil : " << ans << '\n';
    for (path *i = &p[0][0][0][0][0][0]; i->k; i = &p[i->k][w--][i->i][i->j][i->l][i->r])
        rep (j, i->l, i->r) cout << w << ' ' << j << '\n';
    return 0;
}

⭐饼干

通过额外的算法确定DP状态的计算顺序, 排序获得DP顺序, 对状态进行放缩

利用相对大小不变性, 把第i个孩子获得饼干数放缩到1,在考虑前面有多少人和自己数量相等

ll f[31][5001], a[31];
PLL g[31];

int main() {
    IOS; cin >> n >> m; memset(f, 0x3f, sizeof f); f[0][0] = 0;
    rep (i, 1, n) cin >> g[i].fi, g[i].se = i; sort(g + 1, g + n + 1, greater<PLL>());
    rep (i, 1, n) g[i].fi += g[i - 1].fi;
    rep (i, 1, n) rep (j, i, m) {
        if (j > i) f[i][j] = f[i][j - i];
        rep (k, 1, i) umin(f[i][j], f[i - k][j - k] + (g[i].fi - g[i - k].fi) * (i - k));
    }
    cout << f[n][m] << '\n';
    for (int i = n, j = m, h = 0; i; ) {
        while (j > i && f[i][j] == f[i][j - i]) ++h, j -= i;
        per (k, i, 1) if (f[i][j] == f[i - k][j - k] + (g[i].fi - g[i - k].fi) * (i - k))
            while (k--) a[g[i].se] = 1 + h, --j, --i;
    }
    rep (i, 1, n) cout << a[i] << ' ';
    return 0;
}

背包

数字组合

ll f[10001], a[101];

int main() {
    IOS; cin >> n >> m; f[0] = 1;
    rep (i, 1, n) cin >> a[i];
    rep (i, 1, n) per (j, m, 1) if (j >= a[i]) f[j] += f[j - a[i]];
    cout << f[m];
    return 0;
}

自然数拆分

int n, m, _, k, cas;
long long f[4001];

int main() {
    IOS; cin >> n; f[0] = 1;
    rep (i, 1, n) rep (j, i, n) f[j] = (f[j] + f[j - i]) % mod;
    cout << (f[n] - 1ll + mod) % mod;
    return 0;
}

陪审团

int p[201], q[201], f[201][21][801];

int main() {
	IOS;
	while (cin >> n >> m, n || m) {
		memset(f, -1, sizeof f); f[0][0][400] = 0; cout << "Jury #" << ++cas << '\n';
		rep(i, 1, n) cin >> p[i] >> q[i];
		rep(i, 1, n) rep(j, 0, min(i, m)) rep(k, 0, j * 20 + 400) {
			f[i][j][k] = f[i - 1][j][k];
			if (!j || k - p[i] + q[i] < 0 || k - p[i] + q[i] > 800 || f[i - 1][j - 1][k - p[i] + q[i]] == -1) continue;
			umax(f[i][j][k], f[i - 1][j - 1][k - p[i] + q[i]] + p[i] + q[i]);
		}
		int k = 0, sp = 0, sq = 0; VI a;
		for (; k >= 0; ++k) if (~f[n][m][M + k] || ~f[n][m][M - k])
            if (f[n][m][M - k] < f[n][m][M + k]) break;
            else { k = -k; break; }
		for (int i = n, j = m; j; sp += p[i], sq += q[i], k -= p[i] - q[i], a.pb(i--), --j)
			while (f[i][j][k + M] == f[i - 1][j][k + M]) --i;
		cout << "Best jury has value " << sp << " for prosecution and value " << sq << " for defence:\n";
		per(i, a.size() - 1, 0) cout << a[i] << ' ';  cout << "\n\n";
	}
	return 0;
}

硬币

int cnt[N], a[101], c;
bool f[N];

int main() {
    IOS;
    while (cin >> n >> m, k = 0, memset(f, 0, sizeof f), f[0] = 1, n || m) {
        rep (i, 1, n) cin >> a[i];
        rep (i, 1, n) {
            cin >> c; memset(cnt, 0, sizeof cnt);
            rep (j, a[i], m)
                if (f[j]) cnt[j % a[i]] = 0;
                else if (f[j - a[i]] && cnt[j % a[i]] + 1 <= c) f[j] = ++k, ++cnt[j % a[i]];
        }
        cout << k << '\n';
    }
    return 0;
}

区间DP

石子合并

ll f[301][301], a[N];

int main() {
    IOS; cin >> n; memset(f, 0x3f, sizeof f);
    rep (i, 1, n) cin >> a[i], a[i] += a[i - 1], f[i][i] = 0;
    rep (len, 1, n) rep (l, 1, n + 1 - len) rep (k, l, l + len - 2)
        umin(f[l][l + len - 1], f[l][k] + f[k + 1][l + len - 1] + a[l + len - 1] - a[l - 1]);
    cout << f[1][n];
    return 0;
}

多边形

int f[2][101][101], m = -1e9;
bool g[101];

int main() {
    IOS; cin >> n; VI a;  memset(f[0], 0xcf, sizeof f[0]); memset(f[1], 0x3f, sizeof f[1]);
    rep (i, 1, n) {
        char c; cin >> c >> f[0][i][i];
        f[1][i][i] = f[0][i + n][i + n] = f[1][i + n][i + n] = f[0][i][i];
        g[i - 1] = g[i - 1 + n] = c == 't';
    }
    rep (len, 1, n) rep (l, 1, n + n + 1 - len) rep (k, l, l + len - 2) {
        if (g[k]) umax(f[0][l][l + len - 1], f[0][l][k] + f[0][k + 1][l + len - 1]),
            umin(f[1][l][l + len - 1], f[1][l][k] + f[1][k + 1][l + len - 1]);
        else umax(f[0][l][l + len - 1], max(f[0][l][k] * f[0][k + 1][l + len - 1],
            f[1][l][k] * f[1][k + 1][l + len - 1])),
            umin(f[1][l][l + len - 1], min(min(f[0][l][k] * f[0][k + 1][l + len - 1],
            f[1][l][k] * f[1][k + 1][l + len - 1]), min(f[0][l][k] * f[1][k + 1][l + len - 1],
            f[1][l][k] * f[0][k + 1][l + len - 1])));
    }
    rep (i, 1, n)
        if (umax(m, f[0][i][i + n - 1])) VI(1, i).swap(a);
        else if (m == f[0][i][i + n - 1]) a.pb(i);
    cout << m << '\n'; for (auto &i : a) cout << i << ' ';
    return 0;
}

⭐金字塔

一棵树必定是奇数个字母, 对于一个根x

没遍历过其一棵子树, 就会再走一遍x,

对于每棵树[l, r](s[l] == s[r]), 枚举其第最后一颗棵子树即可

保证一个状态的所有决策之间互斥

对于子树[l, k](s[l] == s[k]包含了除最后一棵子树的所有情况), 则

f[l,r] += f[l][k] * f[k + 1][r - 1] ([k + 1, r - 1]是最后一颗子树)

ll f[301][301];
char s[302];

int main() {
    IOS; cin >> s + 1;
    for (n = 1; s[n]; ++n) f[n][n] = 1; --n;
    for (int len = 3; len <= n; len += 2) rep (l, 1, n + 1 - len) if (s[l] == s[l + len - 1])
        for (int k = l; k < l + len - 1; k += 2) if (s[k] == s[l] && s[k + 1] == s[l + len - 2])
            f[l][l + len - 1] = (f[l][l + len - 1] + f[l][k] * f[k + 1][l + len - 2] % mod) % mod;
    cout << f[1][n];
    return 0;
}

树形DP

没有上司的舞会

void dfs(int x) {
    for (auto &y : h[x]) {
        dfs(y); umax(f[x][1], f[x][1] + f[y][0]);
        umax(f[x][0], f[x][0] + max(f[y][1], f[y][0]));
    }
}

int main() {
    IOS; cin >> n;
    rep (i, 1, n) cin >> f[i][1];
    rep (i, 2, n) { int L, K; cin >> L >> K; b[L] = K, h[K].pb(L); }
    rep (i, 1, n) if (!b[i]) dfs(m = i);
    cout << max(f[m][0], f[m][1]);
    return 0;
}

选课

int b[N], f[N][N], d[N];
VI h[N];

void dfs(int x) {
    for (auto &y : h[x]) {
        dfs(y); memcpy(d, f[x], sizeof d);
        if (x) rep (i, 1, m) rep (j, i + 1, m) umax(d[j], f[x][j - i] + f[y][i]);
        else rep (i, 1, m) rep (j, i, m) umax(d[j], f[x][j - i] + f[y][i]);
        memcpy(f[x], d, sizeof d);
    }
}

int main() {
    IOS; cin >> n >> m;
    rep (i, 1, n) cin >> k >> f[i][1], h[k].pb(i);
    dfs(0); cout << *max_element(f[0] + 1, f[0] + m + 1);
    return 0;
}

积蓄程度

别忘了考虑, 换根的时候, 原根为出海口

int dfs(int x, int fa) {
    for (auto &[y, c] : h[x]) if (y ^ fa) f[x] += min(c, dfs(y, x));
    return h[x].size() == 1 && h[x][0].fi == fa ? INF : f[x];
}

void dfss(int x, int fa) {
    for (auto &[y, c] : h[x]) if (y ^ fa) {
        f[y] += h[x].size() == 1 ? c : min(c, f[x] - min(c, f[y]));
        dfss(y, x);
    }
}

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n; rep (i, 1, n) h[i].resize(0); memset(f, 0, sizeof f);
        rep (i, 2, n) { int u, v, c; cin >> u >> v >> c; h[u].pb(v, c); h[v].pb(u, c); }
        dfs(1, 0); dfss(1, 0); cout << *max_element(f + 1, f + 1 + n) << '\n';
    }
    return 0;
}

环形与后效性处理

⭐休息时间

强制改变设定初始状态, 与尾状态拼接

同时利用好 0, 1 表示当前阶段尾状态是否醒 or 睡

int f[2][N][2], a[N], ans;

int work() {
    rep (i, 2, n) rep (j, 0, m) {
        ...
    }
    return max(f[n & 1][m][1], f[n & 1][m][0]);
}

int main() {
    IOS; cin >> n >> m;
    rep (i, 1, n) cin >> a[i];
    memset(f, 0xcf, sizeof f); f[1][0][0] = f[1][1][1] = 0; ans = work();
    memset(f, 0xcf, sizeof f); f[1][1][1] = a[1]; work();
    cout << max(ans, f[n & 1][m][1]);
    return 0;
}

环路运输

int n, a[N], h, t = -1, ans;
PII q[N];

int main() {
    IOS; cin >> n;
    rep (i, 0, n - 1) cin >> a[i];
    rep (i, 0, (n << 1) - 1) {
        while (t >= h && i - q[h].se > n >> 1) ++h;
        if (t >= h) ans = max(ans, a[i % n] + q[h].fi + i);
        while (t >= h && q[t].fi <= a[i % n] - i) --t;
        q[++t] = { a[i % n] - i, i }; 
    }
    cout << ans;
    return 0;
}

⭐坏掉的机器人

行之间无后效性, 行内后效性通过高斯消元求解

事实上, 很多数学期望DP都会采取倒推的方式执行

int n, m, x, y;
double a[N][N];

void gauss(int n, int m) {
    rep (t, 1, m - 2) {
        a[t][t + 1] /= a[t][t]; a[t][m] /= a[t][t]; a[t][t] = 1; 
        a[t + 1][t + 1] -= a[t + 1][t] * a[t][t + 1];
        a[t + 1][m] -= a[t + 1][t] * a[t][m]; a[t + 1][t] = 0;
    }
    a[m - 1][m] /= a[m - 1][m - 1]; a[m - 1][m - 1] = 1;
    per (i, m - 2, 1) a[i][m] -= a[i + 1][m] * a[i][i + 1];
}

int main() {
    IOS; cin >> n >> m >> x >> y;
    if (m == 1) return cout << precision(4) << 2.0 * (n - x) << '\n', 0;
    per (i, n - 1, x) {
        a[1][1] = 2.0 / 3, a[1][2] = -1.0 / 3, a[1][m + 1] = a[1][m + 1] / 3 + 1;
        a[m][m - 1] = -1.0 / 3, a[m][m] = 2.0 / 3, a[m][m + 1] = a[m][m + 1] / 3 + 1;
        rep (j, 2, m - 1)
            a[j][j - 1] = a[j][j + 1] = -0.25, a[j][j] = 0.75, a[j][m + 1] = a[j][m + 1] / 4 + 1;
        gauss(n, m + 1);
    }
    cout << precision(4) << a[y][m + 1];
    return 0;
}

状压DP

蒙德里安的梦想

ll f[12][N];
bool v[N];

int main() {
    IOS;
    while (cin >> n >> m, n || m) {
        memset(v, 0, sizeof v); memset(f, 0, sizeof f); f[0][0] = 1;
        rep (i, 0, (1 << m) - 1) for (int j = -1, k = 0; j < m; j = k++) {
            for (; k < m && !(i >> k & 1); ++k);
            if (!(k - j & 1)) { v[i] = 1; break; }
        }
        rep (i, 1, n) rep (k, 0, (1 << m) - 1) if (f[i - 1][k])
            rep (j, 0, (1 << m) - 1) if (!(k & j) && !v[k | j]) f[i][j] += f[i - 1][k];
        cout << f[n][0] << '\n';
    }
    return 0;
}

炮兵阵地

int f[101][60][60];
char s[101][10];
bool v[N];

bool check(int i, int k) {
    rep (j, 0, m - 1) if ((k >> j & 1) && s[i][j] == 'H') return 0;
    return 1;
}

int main() {
    IOS; cin >> n >> m; VI a, b; memset(f, -1, sizeof f); f[0][0][0] = 0;
    rep (i, 0, (1 << m) - 1) rep (j, 0, m - 1) if (i >> j & 1)
        rep (k, j + 1, m - 1) if ((i >> k & 1) && k - j < 3) v[i] = 1;
    rep (i, 1, n) cin >> s[i];
    rep (i, 0, (1 << m) - 1) if (!v[i]) a.pb(i), b.pb(__builtin_popcount(i));
    rep (i, 1, n) rep (k, 0, a.size() - 1) rep (p, 0, a.size() - 1)
        if (!(a[k] & a[p]) && ~f[i - 1][k][p]) rep (j, 0, a.size() - 1)
            if (!(a[k] & a[j]) && !(a[p] & a[j]) && check(i, a[j])) umax(f[i][p][j], f[i - 1][k][p] + b[j]);
    rep (k, 0, a.size() - 1) rep (p, 0, a.size() - 1) umax(_, f[n][k][p]);
    cout << _;
    return 0;
}

⭐宝藏

f[rt][k], 以rt为根, 状态为k 的最小化费, 局部最优, 并不是全局最优

5 5
1 2 1
2 3 1
3 4 1
4 5 2
2 5 5

f[2][2,3,4,5] = 7, 单对于全局 取边(4, 5, 2) 比 (2, 5, 5) 优

故应把树的深度作为dp的状态

int s[12][12];
ll f[13][1 << 12];

int main() {
    IOS; cin >> n >> m; memset(s, 0x3f, sizeof s); memset(f, 0x3f, sizeof f);
    rep (i, 1, m) {
        int u, v, c; cin >> u >> v >> c; --u, --v;
        s[u][v] = s[v][u] = min(s[u][v], c);
    }
    rep (i, 0, n - 1) f[1][1 << i] = 0;
    rep (i, 2, n) rep (x, 1, (1 << n) - 1) if (f[i - 1][x] ^ f[0][0])
        rep (y, x + 1, (1 << n) - 1) if ((y & x) == x) {
            ll res = 0;
            rep (p, 0, n - 1) if ((x ^ y) >> p & 1) {
                int cur = f[0][0];
                rep (q, 0, n - 1) if (q != p && (x >> q & 1)) umin(cur, s[p][q]);
                res = cur < f[0][0] ? res + cur : -1;
            }
            if (res ^ -1) umin(f[i][y], f[i - 1][x] + res * (i - 1));
        }
    ll ans = 1e9;
    rep (i, 2, n) ans = min(ans, f[i][(1 << n) - 1]);
    cout << (ans == 1e9 ? 0 : ans);
    return 0;
}

倍增DP

⭐开车旅行

ll a[N], f[M][N][2], da[M][N][2], db[M][N][2];

void init() {
    set<PII> st; f[0][n - 1][1] = n; db[0][n - 1][1] = abs(a[n] - a[n - 1]);
    st.insert({ a[n], n }); st.insert({ a[n - 1], n - 1 });
    per (i, n - 2, 1) {
        auto it = st.insert({ a[i], i }).fi, nxt = it, pre = it;
        if (it == st.begin()) f[0][i][1] = (*++nxt).se, f[0][i][0] = (*++nxt).se;
        else if (it == st.end()) f[0][i][1] = (*--pre).se, f[0][i][0] = (*--pre).se;
        else {
            if (a[i] - (*--pre).fi > (*++nxt).fi - a[i]) {
                f[0][i][1] = nxt->se;
                if (nxt == st.end() || (*++nxt).fi - a[i] >= a[i] - pre->fi) f[0][i][0] = pre->se;
                else f[0][i][0] = nxt->se;
            }
            else if (a[i] - pre->fi < nxt->fi - a[i]) {
                f[0][i][1] = pre->se;
                if (pre == st.begin() || a[i] - (*--pre).fi > nxt->fi - a[i]) f[0][i][0] = nxt->se;
                else f[0][i][0] = pre->se;
            }
            else f[0][i][0] = nxt->se, f[0][i][1] = pre->se;
        }
        da[0][i][0] = abs(a[i] - a[f[0][i][0]]), db[0][i][1] = abs(a[i] - a[f[0][i][1]]);
    }
    rep (i, 1, n) {
        f[1][i][1] = f[0][f[0][i][1]][0], f[1][i][0] = f[0][f[0][i][0]][1];
        da[1][i][0] = da[0][i][0], da[1][i][1] = da[0][f[0][i][1]][0];
        db[1][i][1] = db[0][i][1], db[1][i][0] = db[0][f[0][i][0]][1];
    }
    rep (i, 2, 16) rep(j, 1, n) {
        f[i][j][1] = f[i - 1][f[i - 1][j][1]][1], f[i][j][0] = f[i - 1][f[i - 1][j][0]][0];
        da[i][j][0] = da[i - 1][j][0] + da[i - 1][f[i - 1][j][0]][0];
        da[i][j][1] = da[i - 1][j][1] + da[i - 1][f[i - 1][j][1]][1];
        db[i][j][0] = db[i - 1][j][0] + db[i - 1][f[i - 1][j][0]][0];
        db[i][j][1] = db[i - 1][j][1] + db[i - 1][f[i - 1][j][1]][1];
    }
}

PLL calc(int s, int x) {
    ll la = 0, lb = 0;
    per (i, 16, 0) if (f[i][s][0] && la + lb + da[i][s][0] + db[i][s][0] <= x)
        la += da[i][s][0], lb += db[i][s][0], s = f[i][s][0];
    return { la, lb };
}

int main() {
    IOS; cin >> n; rep(i, 1, n) cin >> a[i]; init();
    int s, x; cin >> x; double r = 2e9;
    rep(i, 1, n) {
        auto c = calc(i, x);
        if (c.se && umin(r, c.fi / (double)c.se)) s = i;
    }
    cout << s << '\n';
    for (cin >> _; _; --_) {
        cin >> s >> x; auto c = calc(s, x);
        cout << c.fi << ' ' << c.se << '\n';
    }
    return 0;
}

⭐计算重复

注意当 n1 >= f[j][i] 时 i 不用(--i)

int n1, n2;
ll f[105][20];
char s[105], t[105];

bool check() {
    set<char> st;
    rep (i, 0, n - 1) st.insert(s[i]);
    rep (i, 0, m - 1) if (!st.count(t[i])) return 1;
    return 0;
}

int main() {
    IOS;
    while (cin >> t >> n2 >> s >> n1) {
        n = strlen(s), m = strlen(t); n1 *= n; _ = 0;
        rep (i, 0, n - 1) f[i][0] = 0;
        if (check()) { cout << "0\n"; continue; }
        rep (i, 0, n - 1) rep (j, 0, m - 1) while (s[(++f[i][0] + i - 1) % n] != t[j]);
        rep (i, 1, 19) rep (j, 0, n - 1) 
            f[j][i] = f[j][i - 1] + f[(j + f[j][i - 1]) % n][i - 1];
        for (int i = 19, j = 0; ~i; --i) 
            if (f[j][i] <= n1) _ += 1 << i, n1 -= f[j][i], j = (j + f[j][i++]) % n;
        cout << _ / n2 << '\n';
    }
    return 0;
}

数据结构优化DP

清理班次

int ne[N];

int main() {
    IOS; cin >> n >> m;
    rep (i, 1, n) { int l, r; cin >> l >> r; umax(ne[l], r); }
    rep (i, 2, m) umax(ne[i], ne[i - 1]);
    while (k < m && ne[k + 1] >= k + 1) ++_, k = ne[k + 1];
    cout << (k ^ m ? -1 : _);
    return 0;
}

清理班次2

struct BIT {
    struct node { int r, l, val, tag; } tr[N * 20];
    void push_up(int rt) { tr[rt].val = min(tr[rt << 1].val, tr[rt << 1 | 1].val); }
    void push_down(int rt) {
        umin(tr[rt << 1].val, tr[rt].tag); umin(tr[rt << 1 | 1].val, tr[rt].tag);
        umin(tr[rt << 1].tag, tr[rt].tag); umin(tr[rt << 1 | 1].tag, tr[rt].tag);
        tr[rt].tag = 1e9;
    }
    void build(int rt, int l, int r) {
        tr[rt].l = l, tr[rt].r = r, tr[rt].val = 1e9, tr[rt].tag = 1e9;
        if (l == r) return;
        int mid = l + r >> 1;
        build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r);
    }
    void change(int rt, int l, int r, int k) {
        if (tr[rt].l >= l && tr[rt].r <= r) { umin(tr[rt].val, k); umin(tr[rt].tag, k); return; }
        push_down(rt);int mid =tr[rt].l + tr[rt].r >> 1;
        if (mid >= l) change(rt << 1, l, r, k);
        if (mid < r) change(rt << 1 | 1, l, r, k);
        push_up(rt);
    }
    int ask(int rt, int l, int r) {
        if (tr[rt].l >= l && tr[rt].r <= r) return tr[rt].val;
        push_down(rt); int mid = tr[rt].l + tr[rt].r >> 1;
        int ans = mid >= l ? ask(rt << 1, l, r) : 1e9;
        if (mid < r) umin(ans, ask(rt << 1 | 1, l, r));
        return push_up(rt), ans;
    }
} bit;

struct node { int x, y, c; } a[10005];

int n, m, _, k, cas, e;

int main() {
    IOS; cin >> n >> m >> e; bit.build(1, m, e);
    rep (i, 1, n) cin >> a[i].x >> a[i].y >> a[i].c;
    sort(a + 1, a + 1 + n, [](node& a, node& b) { return a.y < b.y; });
    rep (i, 1, n) {
        if (a[i].x == m) { bit.change(1, m, a[i].y, a[i].c); continue; }
        int res = bit.ask(1, a[i].x - 1, a[i].y - 1);
        if (res != 1e9) bit.change(1, a[i].x, a[i].y, a[i].c + res);
    }
    int res = bit.ask(1, e, e);
    cout << (res == 1e9 ? -1 : res);
    return 0;
}

赤壁之战

int c[N][N], a[N];

void add(int x, int k, int t) { for (; x <= n; x += -x & x) c[t][x] = (c[t][x] + k) % mod; }

int ask(int x, int t) {int ans = 0; for (; x; x -= -x & x) ans = (ans + c[t][x]) % mod; return ans; }

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> m; VI g;
        rep (i, 1, n) cin >> a[i], g.pb(a[i]);
        sort(all(g)); g.erase(unique(all(g)), g.end());
        per (i, g.size(), 1) per (j, g.size(), 1) c[i][j] = 0;
        rep (i, 1, n) {
            a[i] = lower_bound(all(g), a[i]) - g.begin() + 1;
            add(a[i], 1, 1);
            rep (j, 2, m) add(a[i], ask(a[i] - 1, j - 1), j);
        }
        cout << "Case #" << ++cas << ": " << ask(g.size(), m) << '\n';
    }
    return 0;
}

单调队列优化DP

围栏

const int N = 1.6e4 + 5, M = 105;

struct node { int l, p, s; } a[M];

int n, m, _, k, cas;
int f[M][N], q[N];

int calc(int i, int k) { return f[i - 1][k] - a[i].p * k; }

int main() {
    IOS; cin >> n >> m;
    rep (i, 1, m) cin >> a[i].l >> a[i].p >> a[i].s;
    sort(a + 1, a + 1 + m, [](node& a, node& b) { return a.s < b.s; });
    rep (i, 1, m) {
        int h = 0, t = -1;
        rep (k, max(a[i].s - a[i].l, 0), a[i].s - 1) {
            while (t >= h && calc(i, q[t]) <= calc(i, k)) --t;
            q[++t] = k;
        } 
        rep (j, 1, n) {
            f[i][j] = max(f[i - 1][j], f[i][j - 1]);
            if (j < a[i].s) continue;
            while (t >= h && q[h] < j - a[i].l) ++h;
            if (h <= t) umax(f[i][j], calc(i, q[h]) + a[i].p * j);
        }
    }
    cout << f[m][n];
    return 0;
}

⭐裁剪序列

单调队列优化, 本质时队列把不必要的转移舍去了

而这道题还需要额外的set来维护队列里的必要转移的最小值

单调队列DP模型 \(F[i] = min_{L(i) \leqslant j \leqslant R(i)} {F[j] + val(i, j)}\)

int n, m, _, k, cas;
int a[N], q[N], h, t = -1;
ll f[N], mx, s;
multiset<ll> st;

int main() {
    IOS; cin >> n >> mx;
    rep(i, 1, n) { cin >> a[i]; if (a[i] > mx) return cout << -1, 0; }
    for (int i = 1, j = 0; i <= n; ++i) {
        s += a[i]; while (s > mx) s -= a[++j];
        while (q[h] < j) st.erase(st.find(f[q[h]] + a[q[h + 1]])), ++h;
        int y = t;
        for (; h <= t && a[q[t]] <= a[i]; --t) st.erase(st.find(f[q[t]] + a[q[t + 1]]));
        if (h <= t && y != t) st.erase(st.find(f[q[t]] + a[q[t + 1]])), st.insert(f[q[t]] + a[i]);
        f[i] = h <= t ? min(*st.begin(), f[j] + a[q[h]]) : f[j] + a[i];
        q[++t] = i; st.insert(f[i] + a[i + 1]); q[t + 1] = i + 1;
    }
    cout << f[n];
    return 0;
}

总结与练习

乌龟棋

卡空间注意循环顺序

int f[41][41][41][41], a[351], b[5];

int main() {
    IOS; cin >> n >> m; memset(f, -1, sizeof f);
    rep (i, 1, n) cin >> a[i];
    rep (i, 1, m) cin >> k, ++b[k]; f[b[1]][b[2]][b[3]][b[4]] = a[1];
    per (x, b[1], 0) per (y, b[2], 0) per (z, b[3], 0) per (t, b[4], 0) {
        int d = (b[1] - x) + (b[2] - y) * 2 + (b[3] - z) * 3 + (b[4] - t) * 4 + 1;
        if (~f[x + 1][y][z][t]) umax(f[x][y][z][t], f[x + 1][y][z][t] + a[d]);
        if (~f[x][y + 1][z][t]) umax(f[x][y][z][t], f[x][y + 1][z][t] + a[d]);
        if (~f[x][y][z + 1][t]) umax(f[x][y][z][t], f[x][y][z + 1][t] + a[d]);
        if (~f[x][y][z][t + 1]) umax(f[x][y][z][t], f[x][y][z][t + 1] + a[d]);
    }
    cout << f[0][0][0][0] << '\n';
    return 0;
}

花店橱窗

ll f[N][N], d[N][N];

int main() {
    IOS; cin >> m >> n; memset(f, 0xcf, sizeof f); f[0][0] = 0;
    rep (i, 1, m) rep (j, 1, n) cin >> d[i][j]; VI ans;
    rep (i, 1, n) rep (j, 0, min(i, m)) {
        f[i][j] = f[i - 1][j];
        if (j) umax(f[i][j], f[i - 1][j - 1] + d[j][i]);
    }
    for (int i = n, j = m; j; ans.pb(i--), --j) while (f[i][j] == f[i - 1][j]) --i;
    cout << f[n][m] << '\n'; per (i, ans.size() - 1, 0) cout << ans[i] << ' ';
    return 0;
}

⭐低买

注意到, a[i] 只能去跟新最长串

eg: 3 4 6, 尽管6可以拼接成 3 6, 但是我们要求的是最长串的种类

再来个7, 最长串是 3 4 5 7, 跟3 6 7没关系, 要放缩规划的空间

即直接将a[i]拼接到最长串上, 低于短串不考虑拼接

严格递减, 则当 a[i] == a[j] 的时候, cnt[i] 会重复奇数cnt[j]

要将 cnt[j] 清0, 或cnt[i] -= cnt[j]

int f[N], a[N], c[N];

int main() {
    IOS; cin >> n;
    rep (i, 1, n) {
        cin >> a[i]; f[i] = 1;
        rep (j, 1, i - 1) if (a[j] > a[i]) umax(f[i], f[j] + 1); else if (a[i] == a[j]) c[j] = 0;
        rep (j, 1, i - 1) if (f[i] == f[j] + 1 && a[j] > a[i]) c[i] += c[j];
        if (!c[i]) c[i] = 1;
    }
    rep (i, 1, n) if (umax(m, f[i])) k = c[i]; else if (m == f[i]) k += c[i];
    cout << m << ' ' << k;
    return 0;
}

⭐旅行

裸的LCS, 主要是输出方案

  1. dfs 通过set减少爆搜空间
  2. dfs, 预处理fa[i][j], fb[i][j], 表示再串a(b)前j个字符最后一次出现字符i的位置, 也就减小了爆搜的空间(更快)
char s[85], t[85];
int f[85][85];
set<string> ans;
set<pair<PII, string>> st;

void dfs(int x, int y, string& cur, int k) {
    if (st.count({ { x, y }, cur})) return;
    st.insert({ {x, y }, cur});
    if (k == -1) { ans.insert(cur); return; } 
    if (s[x] == t[y] && f[x][y] == f[x - 1][y - 1] + 1)
        cur[k] = s[x], dfs(x - 1, y - 1, cur, k - 1);
    if (f[x][y] == f[x][y - 1]) dfs(x, y - 1, cur, k);
    if (f[x][y] == f[x - 1][y]) dfs(x - 1, y, cur, k);
}

int main() {
    IOS; cin >> s + 1 >> t + 1; n = strlen(s + 1), m = strlen(t + 1);
    rep (i, 1, n) rep (j, 1, m)
        f[i][j] = s[i] == t[j] ? f[i - 1][j - 1] + 1 : max(f[i][j - 1], f[i - 1][j]);
    string res(f[n][m], ' '); dfs(n, m, res, f[n][m] - 1);
    for (auto &s : ans) cout << s << '\n';
    return 0;
}

减操作

int a[N];
bool f[N][20001];

int main() {
    IOS; cin >> n >> m; VI ans;
    rep (i, 1, n) cin >> a[i];
    f[1][a[1] + M] = f[2][a[1] - a[2] + M] = 1;
    rep (i, 3, n) rep (j, -M, M) {
        int x = j - a[i], y = j + a[i];
        if (x >= -M && x <= M && f[i - 1][x + M]) f[i][j + M] = 1;
        if (y >= -M && y <= M && f[i - 1][y + M]) f[i][j + M] = 1; 
    }
    for (int i = n, j = n; i > 1; m += a[i], ans.pb(j - i), j = --i)
        while (m - a[i] >= -M && m - a[i] <= M && f[i - 1][m - a[i] + M]) m -= a[i--];
    per (i, ans.size() - 1, 0) { rep (j, 1, ans[i]) cout << 2 << '\n'; cout << "1\n"; }
    return 0;
}

⭐划分大理石

别忘了集合, 还有一种不超过, 不少于

分多段的, 要考虑“第一段”括号序列

即划分成 {A}B、[A]B 或 (A)B,其中 A,B 是子问题,即可避免重复

int a[7], c[6];
bool f[N];

int main() {
    IOS;
    while (cin >> a[1] >> a[2] >> a[3] >> a[4] >> a[5] >> a[6], a[1] || a[2] || a[3] || a[4] || a[5] || a[6]) {
        memset(f, 0, sizeof f); f[0] = 1;
        int s = a[1] + a[2] * 2 + a[3] * 3 + a[4] * 4 + a[5] * 5 + a[6] * 6;
        for (int i = 1; i <= 6; ++i, memset(c, 0, sizeof c)) rep (k, i, s >> 1) {
            c[k % i] = f[k] ? 0 : c[k % i] + 1;
            if (f[k - i] && c[k % i] <= a[i]) f[k] = 1;
        }
        cout << (!(s & 1) && f[s >> 1] ? "Can\n" : "Can't\n");
    }
    return 0;
}

⭐折叠序列

f[l][r] 表示[l, r]的最短折叠长度

d[l][r] 表示[l, r]只含一个折叠的最短即(...)

⭐注意到能[l, k](k < r)折叠 d[l][r],

但 d[l][r] != (k - l + 1) + cnt(折叠数字位数) + 2["()"]

而是 d[l][r] = f[l][k] + cnt(折叠数字位数) + 2["()"]

打印答案时也是, 对于循环[l, k] 也要去打印其折叠串

int n, m, _, k, cas;
int f[N][N], d[N][N], len[N][N];
char s[N];

int work(int l, int r, int k) {
    if ((r - l + 1) % k) return 1000;
    rep (i, k + l, r) if (s[i] != s[(i - l) % k + l]) return 1000;
    int cnt = (r - l + 1) / k; 
    return f[l][l + k - 1] + 3 + cnt / 100 + (cnt > 9);
}

void dfs(int l, int r) {
    if (r - l + 1 <= 4) { rep (i, l, r) cout << s[i]; return; }
    if (f[l][r] == d[l][r]) {
        if (d[l][r] != r - l + 1) cout << (r - l + 1) / len[l][r] << '(';
        if (f[l][l + len[l][r] - 1] == len[l][r])
            rep (i, l, l + len[l][r] - 1) cout << s[i];
        else dfs(l, l + len[l][r] - 1);
        if (len[l][r] != r - l + 1) cout << ')'; return;
    }
    rep (k, l, r - 1) if (f[l][k] + f[k + 1][r] == f[l][r]) {
        dfs(l, k), dfs(k + 1, r); return;
    }
}

int main() {
    IOS; cin >> s + 1; n = strlen(s + 1); memset(f, 0x3f, sizeof f);
    rep (i, 1, n) rep (l, 1, n) {
        d[l][l + i - 1] = i, len[l][l + i - 1] = i;
        rep (k, l, l + i - 2) {
            umin(f[l][l + i - 1], f[l][k] + f[k + 1][l + i - 1]);
            int cur = work(l, l + i - 1, k - l + 1);
            if (umin(d[l][l + i - 1], cur)) len[l][l + i - 1] = k - l + 1;
        }
        umin(f[l][l + i - 1], d[l][l + i - 1]);
    }
    dfs(1, n);
    return 0;
}

能量项链

ll a[N], f[N][N];

int main() {
    cin >> n;
    rep (i, 1, n) cin >> a[i], a[i + n] = a[i];
    rep (len, 2, n) rep (l, 1, n*2-len+1) rep (k, l, l+len-2)
        f[l][l+len-1]=max(f[l][l+len-1],f[l][k]+f[k+1][l+len-1]+a[l]*a[k+1]*a[l+len]);
    ll ans = 0;
    rep (i, 1, n) ans = max(ans, f[i][i + n - 1]);
    cout << ans;
    return 0;
}

⭐棋盘分割

int s[9][9];
ll f[16][9][9][9][9];

int main() {
    IOS; cin >> n; memset(f, 0x3f, sizeof f);
    rep (i, 1, 8) rep (j, 1, 8) {
        cin >> s[i][j]; s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
        rep (x, 1, i) rep (y, 1, j) f[1][x][y][i][j] = sqr(s[i][j] - s[i][y - 1] - s[x - 1][j] + s[x - 1][y - 1]);
    }
    rep (k, 2, n) rep (i, 1, 8) rep (j, 1, 8) rep (x, i, 8) rep (y, j, 8) {
        rep (l, i, x - 1) umin(f[k][i][j][x][y], min(f[1][i][j][l][y] + f[k - 1][l + 1][j][x][y], f[k - 1][i][j][l][y] + f[1][l + 1][j][x][y]));
        rep (h, j, y - 1) umin(f[k][i][j][x][y], min(f[1][i][j][x][h] + f[k - 1][i][h + 1][x][y], f[k - 1][i][j][x][h] + f[1][i][h + 1][x][y]));
    }
    cout << precision(3) << sqrt((double)f[n][1][1][8][8] / n - sqr((double)s[8][8] / n));
    return 0;
}

⭐消木块

单独再开一维记录区间末尾的状态

int n, m, _, f[201][201][201], pre[201], ls[201], cas;
PII a[201];

int dfs(int l, int r, int len) {
    if (~f[l][r][len]) return f[l][r][len];
    if (l == r) return f[l][r][len] = sqr(a[l].se + len);
    f[l][r][len] = dfs(l, r - 1, 0) + sqr(a[r].se + len);
    for (int i = pre[r]; i >= l; i = pre[i])
        umax(f[l][r][len], dfs(l, i, len + a[r].se) + dfs(i + 1, r - 1, 0));
    return f[l][r][len];
}

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n; m = 1; memset(f, -1, sizeof f); memset(ls, 0, sizeof ls);
        rep (i, 1, n) {
            cin >> a[m].fi;
            if (a[m].fi == a[m - 1].fi) ++a[m - 1].se;
            else pre[m] = ls[a[m].fi], ls[a[m].fi] = m, a[m++].se = 1;
        }
        cout << "Case " << ++cas << ": " << dfs(1, m - 1, 0) << '\n';
    }
    return 0;
}

战略游戏

int f[N][2];
VI h[N];
bool vs[N];

void dfs(int x) {
    for (auto& y : h[x]) {
        dfs(y); f[x][1] += min(f[y][1], f[y][0]);
        f[x][0] += f[y][1];
    }
}

int main() {
    while (~scanf("%d", &n)) {
        rep (i, 0, n - 1) h[i].resize(0), f[i][0] = 0, f[i][1] = 1, vs[i] = 0;
        rep (i, 0, n - 1) {
            int u, k, v; scanf("%d:(%d)", &u, &k);
            rep (i, 1, k) scanf("%d", &v), h[u].pb(v), vs[v] = 1;
        }
        int rt = 0; while (vs[rt]) ++rt;
        dfs(rt); printf("%d\n", min(f[rt][0], f[rt][1]));
    }
    return 0;
}

贿赂FIPA

int get() {
    ull c = 0;
    for (int i = 0; s[i]; ++i) c = c * P + s[i];
    return st.count(c) ? st[c] : (st[c] = st.size() + 1, st.size());
}

void dfs(int x) {
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) dfs(y), sz[x] += sz[y];
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) per(j, sz[x], 0)
        per(k, min(sz[y], j), 1) umin(f[x][j], f[x][j - k] + f[y][k]);
    if (x) rep(j, 0, sz[x]) umin(f[x][j], a[x]);
}

int main() {
    while (read(n), n) {
        read(m); unordered_map<ull, int>().swap(st); memset(f, 0x3f, sizeof f); tot = 0;
        rep(i, 0, n) fa[i] = 1, f[i][0] = h[i] = 0, sz[i] = 1;
        rep(i, 1, n) {
            int u, v; scanf("%s", s); scanf("%d", &a[u = get()]);
            for (char c = getchar(); c != '\n'; c = getchar())
                scanf("%s", s), fa[v = get()] = 0, add(u, v);
        }
        rep(i, 1, n) if (fa[i]) add(0, i);
        dfs(0); cout << f[0][m] << '\n';
    }
    return 0;
}

计算机

PII d[N][2];
vector<PII> h[N];

void dfs(int x, int fa) {
    for (auto& [y, c] : h[x]) if (y ^ fa) {
        dfs(y, x);
        if (d[y][0].fi + c >= d[x][0].fi) d[x][1] = d[x][0], d[x][0] = { d[y][0].fi + c, y };
        else if (umax(d[x][1].fi, d[y][0].fi + c)) d[x][1].se = y;
    }
    if (d[x][0].fi < 0) d[x][0] = { 0, x };
}

void dfss(int x, int fa) {
    for (auto& [y, c] : h[x]) if (y ^ fa) {
        int g = d[x][0].se == y;
        if (d[x][g].fi + c >= d[y][0].fi) d[y][1] = d[y][0], d[y][0] = { d[x][g].fi + c, x };
        else if (umax(d[y][1].fi, d[x][g].fi + c)) d[y][1].se = x;
        dfss(y, x);
    }
}

int main() {
    IOS;
    while (cin >> n) {
        memset(d, 0xcf, sizeof d); rep (i, 1, n) h[i].resize(0);
        rep(i, 2, n) { int v, c; cin >> v >> c; h[i].pb(v, c); h[v].pb(i, c); }
        dfs(1, -1); dfss(1, -1); rep(i, 1, n) cout << d[i][0].fi << '\n';
    }
    return 0;
}

异或路径

int n, m, _, k;
int h[N], to[M << 1], ne[M << 1], co[M << 1], tot;
long double cnt[N];

void add(int u, int v, int c) {
    ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c;
}

long double a[N][N];

int gauss(int n, int m) {
    int c = 1, r = 1;
    for (int t = r; c < m && r <= n; ++c, t = r) {
        for (int i = r + 1; i <= n; ++i) if (fabs(a[i][c]) > fabs(a[t][c])) t = i;
        if (fabs(a[t][c]) < eps) continue;
        if (t != r) for (int i = 1; i <= m; ++i) swap(a[t][i], a[r][i]);
        for (int i = c + 1; i <= m; ++i) a[r][i] /= a[r][c]; a[r][c] = 1;
        for (int i = r + 1; i <= n; ++i)
            if (a[i][c]) {
                for (int j = c + 1; j <= m; ++j) a[i][j] -= a[i][c] * a[r][j];
                a[i][c] = 0;
            }
        ++r;
    }
    rep (i, r, n) if (a[i][m]) return -1;
    if (r < m) return 0;
    per (i, m - 2, 1) rep (j, i + 1, m) a[i][m] -= a[j][m] * a[i][j];
    return 1;
}

int main() {
    IOS; cin >> n >> m;
    rep (i, 1, m) {
        int u, v, co; cin >> u >> v >> co;
        add(u, v, co); cnt[u] += 1;
        if (u ^ v) cnt[v] += 1, add(v, u, co);
    }
    double ans = 0;
    rep (i, 0, 29) {
        memset(a, 0, sizeof a);
        rep (j, 1, n - 1) {
            a[j][j] = 1;
            for (int k = h[j], y = to[k]; k; y = to[k = ne[k]]) {
                double w = 1 / cnt[j];
                if ((co[k] >> i) & 1) a[j][y] += w, a[j][n + 1] += w;
                else a[j][y] -= w;
            }
        }
        a[n][n] = 1; gauss(n, n + 1); ans += a[1][n + 1] * (1 << i);
    }
    cout << setiosflags(ios::fixed) << setprecision(3) << ans << '\n';
    return 0;
}

岛和桥

ll d[M][M], a[M], g[M][M][M], s;
PLL f[N][M][M];

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> m; s = 0; memset(d, 0, sizeof d); memset(g, 0, sizeof g);
        rep(i, 0, n - 1) cin >> a[i], s += a[i]; memset(f, 0xcf, sizeof f);
        rep(i, 1, m) {
            int u, v; cin >> u >> v; --u, --v;
            if (u ^ v) d[u][v] = d[v][u] = a[u] * a[v],
                f[1 << u ^ 1 << v][u][v] = f[1 << u ^ 1 << v][v][u] = { d[v][u], 1 };
        }
        rep(i, 0, n - 1) rep(j, 0, n - 1) rep(k, 0, n - 1)
            if (d[i][j] && d[j][k] && d[k][i]) g[i][j][k] = a[i] * a[j] * a[k];
        rep(i, 0, (1 << n) - 1) rep(x, 0, n - 1) if (i >> x & 1) rep(y, 0, n - 1)
            if ((y ^ x) && (i >> y & 1) && d[x][y]) rep(k, 0, n - 1)
                if ((y ^ k) && (x ^ k) && (i >> k & 1) && ~f[i ^ 1 << y][k][x].fi)
                    if (umax(f[i][x][y].fi, f[i ^ 1 << y][k][x].fi + d[x][y] + g[k][x][y]))
                        f[i][x][y].se = f[i ^ 1 << y][k][x].se;
                    else if (f[i][x][y].fi == f[i ^ 1 << y][k][x].fi + d[x][y] + g[k][x][y])
                        f[i][x][y].se += f[i ^ 1 << y][k][x].se;
        PII ans = { 0, 0 };
        rep(i, 0, n - 1) rep(j, 0, n - 1) if (i ^ j)
            if (umax(ans.fi, f[(1 << n) - 1][i][j].fi)) ans.se = f[(1 << n) - 1][i][j].se;
            else if (ans.fi == f[(1 << n) - 1][i][j].fi) ans.se += f[(1 << n) - 1][i][j].se;
        cout << ans.fi + (ans.se ? s : 0) << ' ' << ans.se / 2 << '\n';
    }
    return 0;
}

玉米田

int f[13][N], g[13];
bool v[N];

int main() {
    IOS; cin >> n >> m; f[0][0] = 1;
    rep (i, 1, n) rep(j, 0, m - 1) cin >> k, g[i] ^= k << j;
    rep (i, 0, (1 << m) - 1) rep (j, 0, m - 1) if (i >> j & 1)
        rep (k, j + 1, m - 1) if ((i >> k & 1) && k - j < 2) v[i] = 1;
    rep (i, 1, n) rep (k, 0, (1 << m) - 1) if (f[i - 1][k])
        rep (j, 0, (1 << m) - 1) if (!(k & j) && !v[j] && (j & g[i]) == j)
            f[i][j] = (f[i][j] + f[i - 1][k]) % mod;
    rep (i, 0, (1 << m) - 1) _ = (_ + f[n][i]) % mod; cout << _;
    return 0;
}

芯片

int f[2][59049], p[11];
bool v[N][M];

void dfs(int k, int st, int c, int q, int num) {
    if (q == m) { umax(f[k & 1][c], f[k & 1 ^ 1][st] + num); umax(cas, f[k & 1][c]); return; }
    if (st / p[q] % 3) { dfs(k, st, c + p[q] * (st / p[q] % 3 - 1), q + 1, num); return; }
    if (k < n && q < m-1 && !v[k][q] && !v[k][q + 1] && !v[k + 1][q] && !v[k + 1][q + 1] && !(st/p[q+1]%3)) {
        if (q < m - 2 && !v[k][q + 2] && st / p[q + 2] % 3 == 0 && !v[k + 1][q + 2])
            dfs(k, st, c + p[q] * 13, q + 3, num + 1);
        if (k < n - 1 && !v[k + 2][q] && !v[k + 2][q + 1]) dfs(k, st, c + p[q] * 8, q + 2, num + 1);
    }
    dfs(k, st, c, q + 1, num);
}

int main() {
    IOS; p[0] = 1; rep(i, 1, 10) p[i] = p[i - 1] * 3; memset(f[1], -1, sizeof f[1]);
    for (cin >> _; _; --_) {
        memset(f[n & 1], -1, sizeof f[0]); f[0][0] = 0; cas = -1;
        cin >> n >> m >> k; memset(v, 0, sizeof v);
        rep(i, 1, k) { int x, y; cin >> x >> y; v[x][--y] = 1; }
        rep(i, 1, n) rep(j, 0, p[m] - 1) if (~f[i & 1 ^ 1][j])
            dfs(i, j, 0, 0, 0), f[i & 1 ^ 1][j] = -1;
        cout << cas << '\n';
    }
    return 0;
}

围栏障碍训练场

struct BIT {
    struct node { int l, r, val, tag; } tr[N * 20];
    void push_down(int rt) {
        if (!tr[rt].tag) return;
        tr[rt << 1].val = tr[rt << 1 | 1].val =
            tr[rt << 1].tag = tr[rt << 1 | 1].tag = tr[rt].tag;
        tr[rt].tag = 0; 
    }
    void build(int rt, int l, int r) {
        tr[rt].l = l, tr[rt].r = r, tr[rt].val = tr[rt].tag = 0;
        if (l == r) return;
        int mid = l + r >> 1; build(rt << 1, l, mid);
        build(rt << 1 | 1, mid + 1, r);
    }
    void change(int rt, int l, int r, int k) {
        if (tr[rt].l >= l && tr[rt].r <= r) { tr[rt].tag = tr[rt].val = k; return; }
        int mid = tr[rt].l + tr[rt].r >> 1; push_down(rt);
        if (mid >= l) change(rt << 1, l, r, k);
        if (mid < r) change(rt << 1 | 1, l, r, k);
    }
    int ask(int rt, int d) {
        if (tr[rt].l == tr[rt].r) return tr[rt].val;
        int mid = tr[rt].l + tr[rt].r >> 1; push_down(rt);
        return ask(rt << 1 | (mid < d), d);
    }
} bit;

int n, m, _, k, cas;
PII a[30005], f[30005];

int main() {
    IOS; cin >> n >> m; bit.build(1, -1e5, 1e5);
    rep (i, 1, n) {
        cin >> a[i].fi >> a[i].se;
        int l = bit.ask(1, a[i].fi), r = bit.ask(1, a[i].se);
        f[i].fi = min(f[l].fi + abs(a[l].fi - a[i].fi), f[l].se + abs(a[l].se - a[i].fi));
        f[i].se = min(f[r].fi + abs(a[r].fi - a[i].se), f[r].se + abs(a[r].se - a[i].se));
        bit.change(1, a[i].fi, a[i].se, i);
    }
    cout << min(f[n].fi + abs(a[n].fi - m), f[n].se + abs(a[n].se - m));
    return 0;
}

图论

最短路

通信线路

int check(int m) {
    deque<pair<int, int>> q; q.push_back({1, 0});
    memset(d, 0x3f, sizeof d); d[1] = 0;
    while (!q.empty()) {
        int x = q.front().first; q.pop_front();
        if (x == n)  return d[x];
        for (auto &y : h[x])
            if (y.second > m && d[y.first] > d[x] + 1) q.push_back({y.first, d[y.first] = d[x] + 1});
            else if (y.second <= m && d[y.first] > d[x]) q.push_front({y.first, d[y.first] = d[x]});
    }
    cout << -1; exit(0); return -1;
}

int main() {
    cin >> n >> m >> k;
    for (int i = 1; i <= m; ++i) {
        int u, v, c; cin >> u >> v >> c;
        h[u].push_back({v, c}); h[v].push_back({u, c});
    }
    int l = 0, r = 1000000;
    while (l < r) {
        int mid = (l + r) >> 1;
        if (check(mid) > k) l = mid + 1;
        else r = mid;
    }
    cout << r;
    return 0;
}

最优贸易

void dij(int k, int s){
    d[k][s] = a[s]; 
    priority_queue<PII> q; q.push({a[s], s});
    while (!q.empty()) {
        PII p = q.top(); q.pop();
        int x = p.second, co = p.first;
        if (k && d[1][x] > co) continue;
        else if (!k && d[0][x] < -co) continue;
        for (int i : h[k][x])
            if (k && d[1][i] < max(a[i], d[1][x])) d[1][i] = max(d[1][x], a[i]), q.push({d[1][i], i});
            else if (!k && d[0][i] > min(a[i], d[0][x])) d[0][i] = min(d[0][x], a[i]), q.push({-d[0][i], i});
    }
}

int main() {
    cin >> n >> m; memset(d[0], 0x3f3f, sizeof d[0]);
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1, u, v, z; i <= m; ++i) {
        cin >> u >> v >> z;
        h[0][u].pb(v); h[1][v].pb(u);
        if (z == 2) h[0][v].pb(u), h[1][u].pb(v);
    }
    dij(0, 1); dij(1, n);
    int ans = 0;
    for (int i = 1; i <= n; ++i) ans = max(ans, d[1][i] - d[0][i]);
    cout << ans;
    return 0;
}

⭐道路与航线

int n, m1, m2, s, f[N], deg[N];
bool v[N];
ll d[N];
vector<pair<int, int>> h[N];
vector<int> a[N];

int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }

void unit(int x, int y) { x = find(x), y = find(y); if (x != y) f[y] = x; }

int main() {
    memset(d, 0x3f, sizeof d);
    cin >> n >> m1 >> m2 >> s;
    for (int i = 1; i <= n; ++i) f[i] = i;
    for (int i = 0; i < m1; ++i) {
        int u, v, c; cin >> u >> v >> c; unit(u, v);
        h[u].emplace_back(v, c); h[v].emplace_back(u, c);
    }
    for (int i = 1; i <= n; ++i) a[find(i)].emplace_back(i);
    for (int i = 0; i < m2; ++i) {
        int u, v, c; cin >> u >> v >> c;
        h[u].emplace_back(v, c); ++deg[f[v]];
    }
    queue<int> qu; qu.push(f[s]); d[s] = 0;
    for (int i = 1; i <= n; ++i) if (i == f[i] && i != f[s] && !deg[i]) qu.push(i);
    while (!qu.empty()) {
        int cur = qu.front(); qu.pop();
        priority_queue<pair<ll, int>> q;
        for (auto &i : a[cur]) q.push({-d[i], i});
        while (!q.empty()) {
            int x = q.top().second; q.pop();
            if (v[x]) continue; v[x] = 1;
            for (auto &y : h[x]) {
                if (d[y.first] > d[x] + y.second) {
                    d[y.first] = d[x] + y.second;
                    if (f[x] == f[y.first]) q.push({-d[y.first], y.first});
                }
                if (f[x] != f[y.first] && --deg[f[y.first]] == 0) qu.push(f[y.first]);
            }
        }
    }
    for (int i = 1; i <= n; ++i)
        if (d[i] >= 1e9) cout << "NO PATH\n";
        else cout << d[i] << '\n';
    return 0;
}

排序

int flo() {
    for (int k = 0; k < n; ++k) for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) if (i != j)
        if (!d[i][j] && d[i][k] && d[k][j]) d[i][j] = 1, ++ans[i];
        else if (d[i][j] && d[j][i]) return -1;
    sort(rk, rk + n, [](int a, int b) { return ans[a] < ans[b]; });
    bool flag = 1;
    for (int i = 0; i < n; ++i) if (i != ans[rk[i]]) return 0;
    return flag;
}

int main() {
    while (cin >> n >> m, n + m) {
        memset(d, 0, sizeof d); int g = 0;
        for (int i = 0; i < n; ++i) ans[i] = 0, rk[i] = i;
        for (int i = 1, u, v; i <= m; ++i) {
            cin >> s; if (g) continue;
            u = s[2] - 'A', v = s[0] - 'A';
            if (d[u][v]) continue; d[u][v] = 1, ++ans[u];
            int res = flo();
            if (res == -1) g = -i;
            else if (res == 1) g = i;
        }
        if (g < 0) cout << "Inconsistency found after " << -g << " relations.\n";
        else if (!g) cout << "Sorted sequence cannot be determined.\n";
        else {
            cout << "Sorted sequence determined after " << g << " relations: ";
            for (int i = 0; i < n; ++i) cout << char('A' + rk[i]); cout << '.' << '\n';
        }
    }
    return 0;
}

观光之旅

void find(int u, int v) {
    if (!f[u][v]) b[++tot] = u;
    else find(u, f[u][v]), find(f[u][v], v);
}

int main() {
    cin >> n >> m; memset(a, 0x3f, sizeof a);
    for (int i = 1; i <= n; ++i) a[i][i] = 0;
    for (int i = 0; i < m; ++i) {
        int u, v, c; cin >> u >> v >> c;
        a[u][v] = a[v][u] = min(a[v][u], c);
    }
    int ans = 0x3f3f3f3f;  memcpy(d, a, sizeof a);
    for (int k = 1; k <= n; ++k) {
        for (int i = 1; i < k; ++i) for (int j = i + 1; j < k; ++j) if (ans > (long long)d[i][j] + a[i][k] + a[k][j])
            ans = d[i][j] + a[i][k] + a[k][j], tot = 0, find(i, j), b[++tot] = j, b[++tot] = k;
        for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) if (d[i][j] > d[i][k] + d[k][j])
            d[i][j] = d[i][k] + d[k][j], f[i][j] = k;
    }
    if (ans == 0x3f3f3f3f) return cout << "No solution.", 0;
    for (int i = 1; i <= tot; ++i) cout << b[i] << ' ';
    return 0;
}

牛站

int u[N], v[N], c[N];
vector<vector<ll>> a;
vector<int> x;

vector<vector<ll>> mul(vector<vector<ll>>& a, vector<vector<ll>>& b) {
    vector<vector<ll>> ans(x.size(), vector<ll>(x.size(), 1e18));
    for (int i = 0; i < x.size(); ++i) for (int j = 0; j < x.size(); ++j) for (int k = 0; k < x.size(); ++k)
        ans[i][j] = min(ans[i][j], a[i][k] + b[k][j]);
    return ans;
}

vector<vector<ll>> qpow(vector<vector<ll>> a, int b) {
    vector<vector<ll>> ans(x.size(), vector<ll>(x.size(), 1e18));
    for (int i = 0; i < x.size(); ++i) ans[i][i] = 0;
    for (; b; b >>= 1, a = mul(a, a)) if (b & 1) ans = mul(ans, a);
    return ans;
}

int main() {
    cin >> n >> t >> s >> e;
    for (int i = 0; i < t; ++i) cin >> c[i] >> u[i] >> v[i], x.push_back(u[i]), x.push_back(v[i]);
    sort(x.begin(), x.end()); x.erase(unique(x.begin(), x.end()), x.end());
    vector<vector<ll>>(x.size(), vector<ll>(x.size(), 1e18)).swap(a);
    for (int i = 0; i < t; ++i) {
        u[i] = lower_bound(x.begin(), x.end(), u[i]) - x.begin();
        v[i] = lower_bound(x.begin(), x.end(), v[i]) - x.begin();
        a[u[i]][v[i]] = a[v[i]][u[i]] = c[i];
    }
    a = qpow(a, n);
    s = lower_bound(x.begin(), x.end(), s) - x.begin();
    e = lower_bound(x.begin(), x.end(), e) - x.begin();
    cout << a[s][e] << '\n';
    return 0;
}

最小生成树

走廊泼水节

struct node { int x, y, c; } e[N];

int n, m, _;
int f[N], sz[N];

int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }

int main() {
    for (cin >> _; _; --_) {
        cin >> n; long long ans = 0;
        for (int i = 1; i <= n; ++i) f[i] = i, sz[i] = 1;
        for (int i = 0; i < n - 1; ++i) cin >> e[i].x >> e[i].y >> e[i].c;
        sort(e, e + n - 1, [](node& a, node& b) { return a.c < b.c; });
        for (int i = 0; i < n - 1; ++i) {
            int x = find(e[i].x), y = find(e[i].y);
            if (x == y) continue;
            ans += (sz[x] * sz[y] - 1ll) * (e[i].c + 1);
            f[y] = x, sz[x] += sz[y];
        }
        cout << ans << "\n";
    }
    return 0;
}

野餐规划

int n, f[N], d[N], ans, s, m;
string t;
unordered_map<string, int> st;

int get(string& s) {
    if (st.count(s)) return st[s];
    return st[s] = st.size(), st.size() - 1;
}

int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }

int main() {
    t = "Park"; get(t); cin >> n; long long ans = 0;
    for (int i = 0; i < n; ++i) {
        cin >> t; e[i].x = get(t); cin >> t >> e[i].c; e[i].y = get(t);
    } sort(e, e + n, [](node& a, node& b) { return a.c < b.c; });
    cin >> s; m = st.size() - 1;
    for (int i = 1; i <= m; ++i) f[i] = i, d[i] = 1e9;
    vector<int> a;
    for (int i = 0; i < n; ++i) {
        int x = find(e[i].x), y = find(e[i].y);
        if (x == y) continue;
        if (x > y) swap(x, y);
        if (!x) { d[y] = min(d[y], e[i].c); continue; }
        if (max(d[x], d[y]) == 1e9) f[y] = x, ans += e[i].c, d[x] = min(d[x], d[y]), --m;
        else a.push_back(i);
    }
    for (int i = 1; i < st.size(); ++i) if (i == f[i]) ans += d[i];
    priority_queue<int> q;
    for (auto& i : a) q.push(max(d[find(e[i].x)], d[find(e[i].y)]) - e[i].c);
    while (m > s) --m, ans -= q.top(), q.pop();
    cout << "Total miles driven: " << ans;
    return 0;
}

沙漠之王

double ans, e[N][N], dis[N];
bool check(double x) {
    for (int i = 0; i < n; ++i) dis[i] = 2e9, v[i] = 0; dis[0] = ans = 0;
    for (int i = 1;  i<= n; ++i) {
        double mi = 2e9; int now;
        for (int j = 0; j < n; ++j) if(!v[j] && dis[j] < mi) mi = dis[j], now = j;
        v[now] = 1; ans += dis[now];
        for (int j = 0; j < n; ++j) dis[j] = min(dis[j], abs(ve[now].z - ve[j].z) - x * e[now][j]);
    }
    return ans > 0;
}
int main() {
    while (cin >> n, n) {
        for (int i = 0, x, y, z; i < n; ++i) cin >> ve[i].x >> ve[i].y >> ve[i].z;
        for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j)
            e[i][j] = sqrt(0.0 + sqr(ve[i].x - ve[j].x) + sqr(ve[i].y - ve[j].y));
        double l = 0, r = 2e9;
        while(r - l > 1e-6) {
            double mid = (l + r) / 2;
            if (check(mid)) l = mid;
            else r = mid;
        }
        cout << setiosflags(ios::fixed) << setprecision(3) << l << '\n';
    }
    return 0;
}

黑暗城堡

void dij()  {
    memset(d, 0x3f, sizeof d); d[1] = 0;
    priority_queue<pair<int, int>> q; q.push({0, 1});
    while (!q.empty()) {
        int x = q.top().second; q.pop();
        if (v[x]) continue; v[x] = 1;
        for (auto &y : h[x]) if (d[y.first] > d[x] + y.second) {
            d[y.first] = d[x] + y.second;
            q.push({ -d[y.first], y.first });
        }
    }
}

int main()  {
    cin >> n >> m; long long ans = 1;
    for (int i = 1; i <= m; ++i) {
        int u, v, c; cin >> u >> v >> c;  
        h[u].emplace_back(v, c); h[v].emplace_back(u, c);
    } dij();
    for (int x = 2; x <= n; ++x) {
        int cur = 0;
        for (auto &y : h[x]) if (d[x] - d[y.first] == y.second) ++cur;
        ans = (ans * cur) % mod;
    }
    cout << ans;
    return 0;
}

树的直径与最近公共祖先

⭐巡逻

struct edge { int t, c, w; };

int n, m, _;
int d[N], s1, s2;
vector<edge> h[N];
pair<int, int> pre[N];

void dfs(int x, int fa, int& s) {
    for (int i = 0; i < h[x].size(); ++i) if (h[x][i].t != fa) {
        d[h[x][i].t] = d[x] + h[x][i].c; pre[h[x][i].t] = { x, i };
        if (d[0] < d[h[x][i].t]) d[0] = d[s = h[x][i].t];
        dfs(h[x][i].t, x, s);
    }
}

int work() {
    d[0] = 0; d[1] = 0; dfs(1, 0, s1);
    d[0] = 0; d[s1] = 0; pre[s1] = { 0, 0 }; dfs(s1, 0, s2);
    for (auto i = pre[s2]; i.first; i = pre[i.first])
        h[i.first][i.second].c = -1, h[h[i.first][i.second].t][h[i.first][i.second].w].c = -1;
    return d[0];
}

void dp(int x, int fa, int& ans) {
    for (auto &y : h[x]) if (y.t != fa) {
        d[y.t] = 0; dp(y.t, x, ans);
        ans = max(ans, d[x] + d[y.t] + y.c);
        d[x] = max(d[x], d[y.t] + y.c);
    }
}

int main() {
    cin >> n >> m; long long ans = n - 1 << 1;
    for (int i = 2; i <= n; ++i) {
        int u, v; cin >> u >> v;
        h[u].push_back({v, 1, h[v].size()}); h[v].push_back({u, 1, h[u].size() - 1});
    }
    ans -= work() - 1; if (m == 2) d[1] = s1 = 0, dp(1, 0, s1), ans -= s1 - 1;
    cout << ans;
    return 0;
}

⭐树网的核

int n, m, _;
int f[N], p, q;
long long d[N], mx, ans = 2e18;
vector<pair<int, int>> h[N];

void dfs(int x, int fa, int& s) {
    for (auto& y : h[x]) if (y.first != fa) {
        d[y.first] = d[x] + y.second; f[y.first] = x;
        if (d[0] < d[y.first]) d[0] = d[s = y.first];
        dfs(y.first, x, s);
    }
}

void work() {
    f[1] = d[1] = 0; dfs(1, 0, p);
    d[0] = f[p] = d[p] = 0; dfs(p, 0, q);
}

void dfs(int x, int fa) {
    for (auto& y : h[x]) if (y.first != fa) {
        mx = max(mx, d[y.first] = d[x] + y.second);
        dfs(y.first, x);
    }
}

int main() {
    IOS; cin >> n >> m;
    for (int i = 1; i < n; ++i) {
        int u, v, c; cin >> u >> v >> c;
        h[u].emplace_back(v, c); h[v].emplace_back(u, c);
    }
    work();
    for (int i = q, j = 0; i; j = i, i = f[i]) for (auto& y : h[i])
        if (y.first != j && y.first != f[i]) mx = max(mx, d[y.first] = y.second), dfs(y.first, i);
    for (int i = q, j = q; i; i = f[i]) {
        while (f[j] && d[i] - d[f[j]] <= m) j = f[j];
        ans = min(ans, max(mx, max(d[j], d[q] - d[i])));
    }
    cout << ans;
    return 0;
}

闇の連鎖

struct STFrom {
    int f[N][20], dep[N], lg[N], t;//N为节点的数量
    vector<int>* h;
    void init(int n, vector<int>* H) {
        t = log2(n - 1) + 1; h = H; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1;
        rep(i, 0, t) f[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (auto& y : h[x]) {
                if (dep[y]) continue;
                dep[y] = dep[x] + 1; f[y][0] = x; q.push(y);
                for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
            }
        }
    }
    int lca(int x, int y) {
        if (dep[x] > dep[y]) swap(x, y);
        for (int k = dep[y] - dep[x], i = lg[k]; ~i; --i) if (k >> i & 1) y = f[y][i];
        if (x == y) return x;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    int dist(int x, int y) { return dep[x] + dep[y] - (dep[lca(x, y)] << 1); }
} ST;

int n, m, _, s[N];
long long ans;
vector<int> h[N];

void dfs(int x, int fa) {
    for (auto& y : h[x]) if (y != fa) {
        dfs(y, x); s[x] += s[y];
        if (s[y] == 1) ++ans;
        else if (!s[y]) ans += m;
    }
}

int main() {
    cin >> n >> m;
    rep(i, 2, n) {
        int u, v; cin >> u >> v;
        h[u].push_back(v); h[v].push_back(u);
    }
    ST.init(n, h); ST.bfs(1);
    rep(i, 1, m) {
        int u, v, z; cin >> u >> v; z = ST.lca(u, v);
        ++s[u], --s[z]; ++s[v], --s[z];
    }
    dfs(1, 0); cout << ans;
    return 0;
}

⭐雨天的尾巴

struct STFrom {
    int f[N][20], dep[N], lg[N], t;//N为节点的数量
    vector<int>* h;
    void init(int n, vector<int>* H) {
        t = log2(n - 1) + 1; h = H; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1;
        rep(i, 0, t) f[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (auto& y : h[x]) {
                if (dep[y]) continue;
                dep[y] = dep[x] + 1; f[y][0] = x; q.push(y);
                for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
            }
        }
    }
    int lca(int x, int y) {
        if (dep[x] > dep[y]) swap(x, y);
        for (int k = dep[y] - dep[x], i = lg[k]; ~i; --i) if (k >> i & 1) y = f[y][i];
        if (x == y) return x;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    int dist(int x, int y) { return dep[x] + dep[y] - (dep[lca(x, y)] << 1); }
} ST;

struct BIT {
    struct node { int l, r, val, mx; } tr[70 * N];
    int rt[N], tot;
    void push_up(int rt) {
        if (tr[tr[rt].l].mx >= tr[tr[rt].r].mx) tr[rt].mx = tr[tr[rt].l].mx, tr[rt].val = tr[tr[rt].l].val;
        else tr[rt].mx = tr[tr[rt].r].mx, tr[rt].val = tr[tr[rt].r].val;
    }
    void change(int& p, int l, int r, int d, int k) {
        if (!p) p = ++tot;
        if (l == r) { tr[p].val = l, tr[p].mx += k; return; }
        int mid = l + r >> 1;
        if (d <= mid) change(tr[p].l, l, mid, d, k);
        else change(tr[p].r, mid + 1, r, d, k);
        push_up(p);
    }
    void merge(int& x, int y, int l, int r) {
        if (!x) { x = y; return; }
        if (!y) return;
        if (l == r) { tr[x].mx += tr[y].mx; return; }
        int mid = l + r >> 1;
        merge(tr[x].l, tr[y].l, l, mid); merge(tr[x].r, tr[y].r, mid + 1, r);
        push_up(x);
    }
    int ask(int x) { return tr[x].val; }
} bit;

int n, m, ans[N];
int x[N], y[N], z[N], w[N];
vector<int> h[N], c;

void dfs(int x, int fa) {
    for (auto& y : h[x]) if (y != fa) {
        dfs(y, x);
        bit.merge(bit.rt[x], bit.rt[y], 1, c.size() - 1);
    }
    ans[x] = bit.ask(bit.rt[x]);
}

int main() {
    IOS; cin >> n >> m; c.push_back(0);
    rep(i, 2, n) {
        int u, v; cin >> u >> v;
        h[u].push_back(v); h[v].push_back(u);
    }
    ST.init(n, h); ST.bfs(1);
    rep(i, 1, m) cin >> x[i] >> y[i] >> w[i], z[i] = ST.lca(x[i], y[i]), c.push_back(w[i]);
    sort(c.begin(), c.end()); c.erase(unique(c.begin(), c.end()), c.end());
    rep(i, 1, m) {
        w[i] = lower_bound(c.begin(), c.end(), w[i]) - c.begin();
        bit.change(bit.rt[x[i]], 1, c.size() - 1, w[i], 1);
        bit.change(bit.rt[y[i]], 1, c.size() - 1, w[i], 1);
        bit.change(bit.rt[z[i]], 1, c.size() - 1, w[i], -1);
        bit.change(bit.rt[ST.f[z[i]][0]], 1, c.size() - 1, w[i], -1);
    }
    dfs(1, 0);
    rep(i, 1, n) cout << c[ans[i]] << '\n';
    return 0;
}

⭐天天爱跑步

这道题之所以不需要权值动态线段树, 和雨天的尾巴不同, 是因为贡献具有可加性

struct STFrom {
    int f[N][20], dep[N], lg[N], t;//N为节点的数量
    vector<int> *h;
    void init(int n, vector<int>* H) {
        t = log2(n - 1) + 1; h = H; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1;
        rep(i, 0, t) f[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (auto &y : h[x]) {
                if (dep[y]) continue;
                dep[y] = dep[x] + 1; f[y][0] = x; q.push(y);
                for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
            }
        }
    }
    int lca(int x, int y) {
        if (dep[x] > dep[y]) swap(x, y);
        for(int k = dep[y] - dep[x], i = lg[k]; ~i; --i) if (k >> i & 1) y = f[y][i];
        if (x == y) return x;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    int dist(int x, int y) { return dep[x] + dep[y] - (dep[lca(x, y)]<<1); }
} ST;

int n, m, w[N], s[N], ans[N], x[N], y[N << 1];
vector<pair<int, int>> a[N], b[N << 1];
vector<int> h[N];

void dfs(int u, int fa) {
    int res = x[ST.dep[u] + w[u]] + y[ST.dep[u] - w[u] + n];
    for (auto &v : h[u]) if (v != fa) dfs(v, u);
    for (auto &i : a[u]) x[i.first] += i.second;
    for (auto &i : b[u]) y[i.first] += i.second;
    ans[u] = x[ST.dep[u] + w[u]] + y[ST.dep[u] - w[u] + n] - res;
}

int main() {
    IOS; cin >> n >> m;
    rep (i, 2, n) {
        int u, v; cin >> u >> v;
        h[u].push_back(v); h[v].push_back(u);
    }
    ST.init(n, h); ST.bfs(1);
    rep (i, 1, n) cin >> w[i];
    rep (i, 1, m) {
        int u, v, z; cin >> u >> v; z = ST.lca(u, v);
        a[u].emplace_back(ST.dep[u], 1);
        a[ST.f[z][0]].emplace_back(ST.dep[u], -1);
        b[v].emplace_back(2 * ST.dep[z] - ST.dep[u] + n, 1);
        b[z].emplace_back(2 * ST.dep[z] - ST.dep[u] + n, -1);
    }
    dfs(1, 0);
    rep (i, 1, n) cout << ans[i] << ' ';
    return 0;
}

⭐异象石(时间戳妙用)

每次添加或删除x, 找到x的p, q(时间戳环上的前驱后继), 贡献为dist(x, p) + dist(x, q) - dist(p, q)

答案 / 2即可

struct STFrom {
    int f[N][20], dep[N], lg[N], t;//N为节点的数量
    vector<pair<int, int>> *h;
    ll d[N];
    void init(int n, vector<pair<int ,int>>* H) {
        t = log2(n - 1) + 1; h = H; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1; d[s] = 0;
        rep(i, 0, t) f[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (auto &y : h[x]) {
                if (dep[y.first]) continue;
                dep[y.first] = dep[x] + 1; f[y.first][0] = x;
                q.push(y.first); d[y.first] = d[x] + y.second;
                for (int j = 1; j <= t; ++j) f[y.first][j] = f[f[y.first][j - 1]][j - 1];
            }
        }
    }
    int lca(int x, int y) {
        if (dep[x] > dep[y]) swap(x, y);
        for(int k = dep[y] - dep[x], i = lg[k]; ~i; --i) if (k >> i & 1) y = f[y][i];
        if (x == y) return x;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    ll dist(int x, int y) { return d[x] + d[y] - (d[lca(x, y)]<<1); }
} ST;

int n, m, dfn[N], df;
vector<pair<int, int>> h[N];
set<pair<int, int>> st;
long long ans = 0;

void dfs(int x, int fa) {
    dfn[x] = ++df;
    for (auto &y : h[x]) if (y.first != fa) dfs(y.first, x);
}

ll get(int x) {
    int p, q;
    auto it = st.lower_bound({dfn[x], x});
    if (it == st.end()) q = st.begin()->second;
    else q = it->second;
    if (it == st.begin()) p = st.rbegin()->second;
    else p = (--it)->second;
    return ST.dist(p, x) + ST.dist(q, x) - ST.dist(p, q);  
}

int main() {
    cin >> n;
    rep (i, 2, n) {
        int u, v, c; cin >> u >> v >> c;
        h[u].emplace_back(v, c); h[v].emplace_back(u, c);
    }
    ST.init(n, h); ST.bfs(1); dfs(1, 0);
    for (cin >> m; m; --m) {
        char op; cin >> op;
        if (op == '+') {
            int x; cin >> x;
            if (!st.empty()) ans += get(x);
            st.insert({dfn[x], x});
        } else if (op == '-') {
            int x; cin >> x;
            st.erase(st.find({dfn[x], x}));
            if (!st.empty()) ans -= get(x);
        } 
        else cout << ans / 2 << '\n';
    }
    return 0;
}

严格次小生成树

const int N = 1e5 + 5, M = 3e5 + 5;

struct STFrom {
    int f[N][20], dep[N], lg[N], t;//N为节点的数量
    ll d[N][20], b[N][20];
    vector<PII> *h;
    void init(int n, vector<PII> *H) {
        t = log2(n - 1) + 1; h = H; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1;
        rep(i, 0, t) f[s][i] = 0, d[s][i] = b[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (auto &y : h[x]) {
                if (dep[y.fi]) continue;
                dep[y.fi] = dep[x] + 1; f[y.fi][0] = x; q.push(y.fi);
                d[y.fi][0] = y.se; b[y.fi][0] = -2e18;
                for (int j = 1; j <= t; ++j) {
                    f[y.fi][j] = f[f[y.fi][j - 1]][j - 1];
                    if (d[f[y.fi][j - 1]][j - 1] > d[y.fi][j - 1])
                        b[y.fi][j] = max(d[y.fi][j - 1], b[f[y.fi][j - 1]][j - 1]),
                        d[y.fi][j] = d[f[y.fi][j - 1]][j - 1];
                    else if (d[f[y.fi][j - 1]][j - 1] == d[y.fi][j - 1])
                        b[y.fi][j] = max(b[y.fi][j - 1], b[f[y.fi][j - 1]][j - 1]),
                        d[y.fi][j] = d[y.fi][j - 1];
                    else b[y.fi][j] = max(b[y.fi][j - 1], d[f[y.fi][j - 1]][j - 1]),
                        d[y.fi][j] = d[y.fi][j - 1];
                }
            }
        }
    }
    void work(PLL& ans, int y, int i) {
        if (d[y][i] > ans.fi) ans.se = max(ans.fi, b[y][i]), ans.fi = d[y][i];
        else if (d[y][i] == ans.fi) umax(ans.se, b[y][i]);
        else umax(ans.se, d[y][i]);
    }
    PLL ask(int x, int y) {
        PLL ans = {-2e18, -2e18};
        if (dep[x] > dep[y]) swap(x, y);
        for(int k = dep[y] - dep[x], i = lg[k]; ~i; --i) if (k >> i & 1)
            work(ans, y, i), y = f[y][i];
        if (x == y) return ans;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) {
            work(ans, x, i); work(ans, y, i);
            x = f[x][i], y = f[y][i];
        }
        work(ans, x, 0); work(ans, y, 0);
        return ans;
    }
} ST;

struct edge {int x, y; ll c; bool f = 0; } e[M];

int n, m, _, k, cas;
int f[N];
ll res, ans = 2e18;
vector<PII> h[N];

int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }

int main() {
    IOS; cin >> n >> m;
    rep (i, 1, m) cin >> e[i].x >> e[i].y >> e[i].c;
    sort(e + 1 , e + 1 + m, [](edge& a, edge& b) { return a.c < b.c; });
    rep (i, 1, n) f[i] = i;
    rep (i, 1, m) {
        int x = find(e[i].x), y = find(e[i].y);
        if (x == y) continue;
        res += e[i].c, e[i].f = 1; f[y] = x;
        h[e[i].x].pb(e[i].y, e[i].c); h[e[i].y].pb(e[i].x, e[i].c);
    }
    ST.init(n, h); ST.bfs(1);
    rep (i, 1, m) if (!e[i].f) {
        auto cur = ST.ask(e[i].x, e[i].y);
        if (cur.fi < e[i].c) umin(ans, res - cur.fi + e[i].c);
        else if(cur.se != -2e18 && cur.se < e[i].c) umin(ans, res - cur.se + e[i].c); 
    }
    cout << ans;
    return 0;
}

疫情控制

AcWing t飞了, stl封装结构体死活过不去, 评测姬不太行, 建议牛客神机

const int N = 5e4 + 5;

int n, m, _, k;
int h[N], ne[N << 1], to[N << 1], co[N << 1], tot;
int f[N][20], dep[N], dist[N], fa[N], t;
bool v[N];
vector<PII> a, b;
VI c;

void add(int u, int v, int c) {
    ne[++tot] = h[u]; h[u] = tot; to[tot] = v; co[tot] = c;
}

void bfs(int s) {
    queue<int> q;
    q.push(s); dep[s] = 1; dist[s] = 0;

    while (!q.empty()) {
        int x = q.front(); q.pop();

        if (!ne[h[x]]) { c.pb(x); continue; }

        for (int i = h[x]; i; i = ne[i]) {
            int y = to[i];
            if (dep[y]) continue;
            dep[y] = dep[x] + 1;
            fa[y] = x == s ? y : fa[x];
            dist[y] = dist[x] + co[i];
            f[y][0] = x;

            for (int j = 1; j <= t; ++j)
                f[y][j] = f[f[y][j - 1]][j - 1];

            q.push(y);
        }
    }
}

int ask(int a, int b) {
    per(i, t, 0)
        if (dist[a] - dist[f[a][i]] <= b) b -= dist[a] - dist[a = f[a][i]];
    return a;
}

void change(int k) {
    if (f[k][0] == 1) return;
    k = f[k][0];
    for (int i = h[k]; i; i = ne[i])
        if (!v[to[i]] && to[i] != f[k][0]) return;
    v[k] = 1;
    change(k);
}

bool check(int mid) {
    vector<PII> s;
    memset(v, 0, sizeof v);

    for (PII i : a) {
        int idx = i.se;
        if (idx == 1) s.pb({ mid, 1 });
        else if (mid <= dist[idx] + dist[fa[idx]]) {
            if (mid < dist[idx] - dist[fa[idx]]) {
                v[idx = ask(idx, mid)] = 1;
                change(idx);
            }
            else if (!v[fa[idx]]) v[fa[idx]] = 1;
            else if (mid > dist[idx]) s.pb({ mid - dist[idx], idx });
        }
        else s.pb({ mid - dist[idx], idx });
    }

    sort(all(s));
    int j = -1;
    for (PII i : b)
        if (!v[i.se]) {
            while (++j < s.size() && s[j].fi < i.fi);
            if (j == s.size()) return 0;
        }
    return 1;
}

int main() {
    IO; cin >> n;
    t = log2(n - 1) + 1;
    rep(i, 2, n) {
        int u, v, c; cin >> u >> v >> c;
        add(u, v, c); add(v, u, c);
    }
    bfs(1);
    cin >> m; a.resize(m);
    rep(i, 0, m - 1) cin >> a[i].se, a[i].fi = dist[fa[a[i].se]] - dist[a[i].se];
    sort(all(a));

    for (int i = h[1]; i; i = ne[i]) b.pb({ dist[to[i]], to[i] });
    sort(all(b));

    int l = 0, r = 0x7f7f7f7f;
    while (l < r) {
        int mid = (ll)l + r >> 1;
        if (check(mid)) r = mid;
        else 
            l = mid + 1;
    }
    cout << l;
    return 0;
}

基环树

⭐岛屿

主要处理最长路径环的问题, 要用单调队列, 拆环不满足递增, 无法尺取

int h[N], ne[M], to[M], co[M], tot;
int q[N << 1];
long long d[N];
bool v[N], g[M];
vector<pair<int, long long>> cir;

void add(int u, int v, int c) { ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c; g[tot] = 1; }

int dfs(int x, int e) {
    if (v[x]) return x;
    int ed = 0; v[x] = 1;
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (g[i] && (i >> 1) != e) {
        int cur = dfs(y, i >> 1);
        if (cur) cir.emplace_back(y, co[i] + cir.back().second), ed = cur, g[i] = g[i ^ 1] = 0;
    }
    return ed == x ? 0 : ed;
}

void dfss(int x, int fa, long long& ans) {
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (g[i] && y != fa) {
        dfss(y, x, ans); ans = max(ans, d[x] + d[y] + co[i]);
        d[x] = max(d[x], d[y] + co[i]);
    }
}

long long work(int x) {
    vector<pair<int, long long>>(1).swap(cir);
    long long ans = 0; dfs(x, 0);
    for (int j = 1; j < cir.size(); ++j) dfss(cir[j].first, 0, ans);
    for (int i = 1, sz = cir.size(); i < sz; ++i)
        cir.emplace_back(cir[i]), cir.back().second += cir[sz - 1].second;
    int h = 0, t = -1; q[++t] = 1;
    for (int i = 2, sz = cir.size() >> 1; i < cir.size(); ++i) {
        while (i - q[h] >= sz) ++h;
        ans = max(ans, d[cir[i].first] + cir[i - 1].second + d[cir[q[h]].first] - cir[q[h] - 1].second);
        while (h <= t && d[cir[q[t]].first] - cir[q[t] - 1].second <= d[cir[i].first] - cir[i - 1].second) --t;
        q[++t] = i;
    }
    return ans;
}

int main() {
    IOS; cin >> n; tot = 1; long long ans = 0;
    for (int i = 1; i <= n; ++i) {
        int v, c; cin >> v >> c;
        add(i, v, c); add(v, i, c);
    }
    for (int i = 1; i <= n; ++i) if (!v[i]) ans += work(i);
    cout << ans;
    return 0;
}

⭐创世纪

考虑有向边(a[i], i)拆环之后就是普通的树形dp

所以我们在普通的树形dp之上在考虑拆掉环上的边(a[rt], rt)对答案的影响

无非是当选择 a[rt] 参与时, 需要有一个孩子不参与, 而我们把 rt 这个孩子参不参与忽略了

所以可以进行两边树形dp, 其中一次强制假设 rt 不参与,

则 f[a[rt]][1] 不需要有孩子不参与限制就好了(rt已经不选了, 这次只能取 f[rt][0], 舍去f[rt][1]选项即可)

int n, a[N], rt, f[N][2], ans;
int h[N], to[N], ne[N], tot;
bool v[N];

void add(int u, int v) { ne[++tot] = h[u]; to[h[u] = tot] = v; }

int solve(int u, bool t) {
    f[u][0] = 0; v[u] = 1;
    int res, c = 1e9;
    for (int i = h[u], y = to[i]; i; y = to[i = ne[i]]) if (y != rt)
        res = solve(y, t), c = min(c, res - f[y][0]), f[u][0] += res;
    f[u][1] = f[u][0] - c + 1;
    if (t && u == a[rt]) f[u][1] += c;
    return max(f[u][0], f[u][1]);
}

int work(int u) {
    for (rt = u; !v[a[rt]]; v[rt] = 1, rt = a[rt]);
    int ans = solve(rt, 0); solve(rt, 1);
    return max(ans, f[rt][0]);    
}

int main() {
    cin >> n;
    for (int i = 1; i<= n; ++i) cin >> a[i], add(a[i], i);
    for (int i = 1; i<= n; ++i) if (!v[i]) ans += work(i);
    cout << ans;
    return 0;
}

⭐Freda的传呼机

const int N = 1e4 + 5;

struct STFrom {
    static const int N = 2e4 + 5;
    int f[N][20], dep[N], lg[N], t, dis[N];//N为节点的数量
    vector<pair<int, int>>* h;
    void init(int n, vector<pair<int, int>>* H) {
        t = log2(n - 1) + 1; h = H; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1; dis[s] = 0;
        rep(i, 0, t) f[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (auto& [y, c] : h[x]) {
                if (dep[y]) continue; dis[y] = dis[x] + c;
                dep[y] = dep[x] + 1; f[y][0] = x; q.push(y);
                for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
            }
        }
    }
    int lca(int x, int y) {
        if (dep[x] > dep[y]) swap(x, y);
        for (int k = dep[y] - dep[x]; ~lg[k]; k ^= 1 << lg[k]) y = f[y][lg[k]];
        if (x == y) return x;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    int dist(int x, int d) {
        for (; ~lg[d]; d ^= 1 << lg[d]) x = f[x][lg[d]];
        return x;
    }
} ST;

int n, m, _, k, cas;
int dfn[N], low[N], df, st[N], top;
int sum[N << 1], d[N], vcnt, fa[N];
vector<pair<int, int>> h[N], g[N << 1];

void work(int x, int y, int c) {
    sum[y] = c; swap(sum[++vcnt + n], sum[x]);
    for (int i = y; x ^ i; i = fa[i]) sum[fa[i]] = sum[i] + d[i];
    swap(sum[x], sum[vcnt + n]);
    g[vcnt + n].emplace_back(x, 0), g[x].emplace_back(vcnt + n, 0);
    for (int i = y; x ^ i; i = fa[i]) {
        int c = min(sum[i], sum[vcnt + n] - sum[i]);
        g[vcnt + n].emplace_back(i, c), g[i].emplace_back(vcnt + n, c);
    }
}

void tarjan(int x) {
    dfn[x] = low[x] = ++df, st[++top] = x;
    for (auto& [y, c] : h[x])
        if (!dfn[y]) {
            d[y] = c; fa[y] = x; tarjan(y); low[x] = min(low[x], low[y]);
            if (dfn[x] <= low[y]) {
                for (auto& [z, c] : h[x]) if (z == st[top]) work(x, z, c);
                while (y != st[top--]);
            }
        }
        else low[x] = min(low[x], dfn[y]);
}

int main() {
    cin >> n >> m >> k;
    for (int i = 1; i <= m; ++i) {
        int u, v, c; cin >> u >> v >> c;
        h[u].emplace_back(v, c); h[v].emplace_back(u, c);
    }
    tarjan(1); ST.init(vcnt + n, g); ST.bfs(1);
    for (int i = 1; i <= k; ++i) {
        int x, y, z; cin >> x >> y; z = ST.lca(x, y);
        if (z <= n) cout << ST.dis[x] + ST.dis[y] - ST.dis[z] * 2 << '\n';
        else {
            int a = ST.dist(x, ST.dep[x] - ST.dep[z] - 1);
            int b = ST.dist(y, ST.dep[y] - ST.dep[z] - 1);
            int ans = min(abs(sum[a] - sum[b]), sum[z] - abs(sum[a] - sum[b]));
            cout << ans + ST.dis[x] - ST.dis[a] + ST.dis[y] - ST.dis[b] << '\n';
        }
    }
    return 0;
}

负环与差分约束

观光奶牛

bool check(int s, double mid) {
    for (int i = 1; i <= n; ++i) d[i] = 2e9, v[i] = 0;
    d[s] = 0; dep[s] = v[s] = 1;
    stack<int> st; st.push(s);
    while (!st.empty()) {
        int x = st.top(); st.pop(); v[x] = 0;
        for (int i= h[x], y = to[i]; i; y = to[i = ne[i]]) if (d[y] >= d[x] + mid * co[i] - f[x]) {
            d[y] = d[x] + mid * co[i] - f[x], dep[y] = dep[x] + 1;
            if (dep[y] > n) return 1;
            if (!v[y]) st.push(y), v[y] = 1;
        }
    }
    return 0;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> f[i];
    for (int i = 0; i < m; ++i) {
        int u, v, c; cin >> u >> v >> c;
        add(u, v, c);
    }
    double l = 0, r = 1000;
    while (r - l > 1e-6) {
        double mid = (l + r) / 2;
        if (check(1, mid)) l = mid;
        else r = mid;
    }
    cout << setiosflags(ios::fixed) << setprecision(2) << l;
    return 0;
}

区间

#include <bits/stdc++.h>
using namespace std;

const int N = 5e4 + 5;

struct node { int a, b, c; } p[N];

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

void add(int x, int k) { for (; x <= 5e4; x += -x & x) c[x] += k; }

int ask(int x) { int ans = 0; for (; x; x -= -x & x) ans += c[x]; return ans; }

int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> p[i].a >> p[i].b >> p[i].c;
    sort(p + 1, p + 1 + n, [](node& a, node& b) { return a.b ^ b.b ? a.b < b.b : a.c > b.c; });
    for (int i = 1; i <= 5e4; ++i) f[i] = i;
    for (int i = 1; i <= n; ++i) {
        p[i].c -= ask(p[i].b) - ask(p[i].a - 1);
        for (int j = find(p[i].b); p[i].c > 0; j = f[j], --p[i].c, ++m) add(j, 1), f[j] = find(j - 1);
    }
    cout << m;
    return 0;
}

Tarjan算法与无向图连通性

B城

void tarjan(int x) {
    dfn[x] = low[x] = ++df; int sum = sz[x] = 1, c = 0;
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]])
        if (!dfn[y]) {
            tarjan(y); low[x] = min(low[x], low[y]); sz[x] += sz[y];
            if (low[y] >= dfn[x]) ans[x] += (long long)sz[y] * (n - sz[y]), ++c, sum += sz[y];
        }
        else low[x] = min(low[x], dfn[y]);
    if (c > 1 || (c && x != 1)) ans[x] += (long long)sum * (n - sum) + n - 1;
    else ans[x] = n - 1 << 1;
}

int main() {
    IOS; cin >> n >> m; tot = 1;
    for (int i = 1; i <= m; ++i) {
        int u, v; cin >> u >> v;
        if (u != v) add(u, v); add(v, u);
    }
    tarjan(1);
    for (int i = 1; i <= n; ++i) cout << ans[i] << '\n';
    return 0;
}

⭐网络

缩点建图之后爬树

struct STFrom {
    int f[N][20], dep[N], lg[N], t;//N为节点的数量
    int *h, *ne, *to;
    void init(int n, int* H, int* Ne, int* To) {
        t = log2(n - 1) + 1; h = H, ne = Ne, to = To; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1;
        rep(i, 0, t) f[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) {
                if (dep[y]) continue;
                dep[y] = dep[x] + 1; f[y][0] = x; q.push(y);
                for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
            }
        }
    }
    int lca(int x, int y) {
        if (dep[x] > dep[y]) swap(x, y);
        for(int k = dep[y] - dep[x], i = lg[k]; ~i; --i) if (k >> i & 1) y = f[y][i];
        if (x == y) return x;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    int dist(int x, int y) { return dep[x] + dep[y] - (dep[lca(x, y)]<<1); }
} ST;

int n, m, ecnt, c[N], f[N];
int h[N], to[M], ne[M], tot;
int nh[N], nto[M], nne[M], ntot;
int dfn[N], low[N], df, st[N], top;

void add(int u, int v, int* h, int* ne, int* to, int& tot) {
    ne[++tot] = h[u]; to[h[u] = tot] = v;
}

void tarjan(int x, int bian) {
    dfn[st[++top] = x] = low[x] = ++df;
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]])
        if (!dfn[y]) tarjan(y, i), umin(low[x], low[y]);
        else if (i != (bian ^ 1)) low[x] = min(low[x], dfn[y]);
    if (low[x] == dfn[x]) {
        ++ecnt; int y;
        do c[y = st[top--]] = ecnt; while (y != x);
    }
}

int ff(int x) { return x == f[x] ? x : f[x] = ff(f[x]); }

int main() {
    IOS; int cas = 0;
    while (cin >> n >> m, n || m) {
        ecnt = ntot = df = 0; tot = 1;
        rep (i, 1, n) h[i] = nh[i] = dfn[i] = low[i] = 0;
        rep (i, 1, m) {
            int u, v; cin >> u >> v;
            if (u == v) continue;
            add(u, v, h, ne, to, tot); add(v, u, h, ne, to, tot);
        }
        tarjan(1, 0);
        rep (i, 2, tot) {
            int x = to[i ^ 1], y = to[i];
            if (c[x] ^ c[y]) add(c[x], c[y], nh, nne, nto, ntot);
        }
        ST.init(n, nh, nne, nto); ST.bfs(1);
        rep (i, 1, ecnt) f[i] = i;
        cout << "Case " << ++cas << ":\n";
        for (cin >> m; m; --m) {
            int u, v, z; cin >> u >> v; u = ff(c[u]), v = ff(c[v]); z = ST.lca(u, v);
            while (u ^ z) f[u] = ST.f[u][0], --ecnt, u = ff(u);
            while (v ^ z) f[v] = ST.f[v][0], --ecnt, v = ff(v);
            cout << ecnt - 1 << '\n';
        }
        cout << '\n';
    }
    return 0;
}

⭐圆桌骑士

点双联通分量中若存在奇环, 在此点双中任何两点都被至少一个奇环覆盖

void tarjan(int x) {
    dfn[x] = low[x] = ++df; st[++top] = x;
    for (int y = 1; y <= n; ++y) if (h[x][y])
        if (!dfn[y]) {
            tarjan(y); low[x] = min(low[x], low[y]);
            if (dfn[x] <= low[y]) {
                vcc.emplace_back(vector<int>(1, x)); int z;
                do vcc.back().push_back(z = st[top--]); while (z != y);
            }
        }
        else low[x] = min(low[x], dfn[y]);
}

bool dfs(int x, int c) {
    col[x] = c;
    for (int y = 1; y <= n; ++y) {
        if (!h[x][y] || !g[y] || (col[y] && col[y] != c)) continue;
        if (col[y] || dfs(y, 3 - c)) return 1;
    }
    return 0;
}

int main() {
    IOS;
    while (cin >> n >> m, n || m) {
        for (int i = 1; i < n; ++i) for (int j = i + 1; j <= n; ++j) h[i][j] = h[j][i] = 1;
        memset(dfn, 0, sizeof dfn); df = 0;
        vector<vector<int>>().swap(vcc); 
        for (int i = 1; i <= m; ++i) {
            int u, v; cin >> u >> v;
            h[u][v] = h[v][u] = 0;
        }
        for (int i = 1; i <= n; ++i) if (!dfn[i]) top = 0, tarjan(i);
        for (auto &i : vcc) {
            for (auto &j : i) g[j] = 1, col[j] = 0;
            bool f = dfs(i[0], 1);
            for (auto &j : i) g[j] = 0, v[j] = v[j] | f;
        }
        for (int i = 1, j = n; i <= j; ++i) n -= v[i], v[i] = 0;
        cout << n << '\n';
    }
    return 0;
}

看牛

int h[N], ne[M << 1], to[M << 1], tot, hc[N];
int st[M << 2], ans[M << 2], top, t;
bool vis[M << 1];

void add(int u, int v) { ne[++tot] = h[u]; to[h[u] = tot] = v; }

void eulerc() {
    st[++top] = 1;
    for (int i = 1; i <= n; ++i) hc[i] = h[i];
    while (top) {
        int x = st[top];
        while (hc[x] && vis[hc[x]]) hc[x] = ne[hc[x]];
        if (hc[x]) {
            st[++top] = to[hc[x]];
            vis[hc[x]] = 1;
            hc[x] = ne[hc[x]];
        }
        else --top, ans[++t] = x; 
    }
}

int main() {
    cin >> n >> m; tot = 1;
    for (int i = 1; i <= m; ++i) {
        int u, v; cin >> u >> v;
        add(u, v); add(v, u);
    }
    eulerc();
    for (int i = t; i; --i) cout << ans[i] << '\n';
    return 0;
}

Tarjan算法与有向图连通性

学校网络

void tarjan(int x) {
    dfn[x] = low[x] = ++df, inst[st[++top] = x] = 1;
    for (auto &y : h[x])
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (inst[y]) low[x] = min(low[x], dfn[y]);
    if (dfn[x] == low[x]) {
        ++scnt; int z;
        do inst[z = st[top--]] = 0, c[z] = scnt; while (z != x);
    }
}

int main() {
    cin >> n; int ans1 = 0, ans2 = 0;
    for (int i = 1; i <= n; ++i) while (cin >> m, m) h[i].push_back(m);
    for (int i = 1; i <= n; ++i) if (!dfn[i]) top = 0, tarjan(i);
    for (int x = 1; x <= n; ++x) for (auto &y : h[x]) if (c[x] != c[y]) ++indeg[c[y]], ++outdeg[c[x]];
    for (int i = 1; i <= scnt; ++i) { if (!indeg[i]) ++ans1; if (!outdeg[i]) ++ans2; }
    cout << ans1 << '\n' << (scnt == 1 ? 0 : max(ans1, ans2));
    return 0;
}

银河

void tarjan(int x) {
    dfn[x] = low[x] = ++df, inst[st[++top] = x] = 1;
    for (auto &[y, c] : h[x])
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (inst[y]) low[x] = min(low[x], dfn[y]);
    if (dfn[x] == low[x]) {
        ++scnt; int z;
        do inst[z = st[top--]] = 0, c[z] = scnt; while (z != x);
    }
}

int main() {
    IOS; cin >> n >> m; long long ans = 0;
    for (int i = 0; i < m; ++i) {
        int u, v, c; cin >> c >> u >> v;
        switch (c) {
            case 1 : h[u].emplace_back(v, 0), h[v].emplace_back(u, 0); break;
            case 2 : h[u].emplace_back(v, 1); break;
            case 3 : h[v].emplace_back(u, 0); break;
            case 4 : h[v].emplace_back(u, 1); break;
            case 5 : h[u].emplace_back(v, 0); break;
        }
    }
    for (int i = 1; i <= n; ++i) h[n + 1].emplace_back(i, 1); tarjan(n + 1);
    for (int x = 1; x <= n + 1; ++x) for (auto &[y, co] : h[x])
        if (c[x] == c[y] && co) return cout << -1, 0;
        else if (c[x] != c[y]) g[c[x]].emplace_back(c[y], co), ++deg[c[y]];
    st[top = 1] = c[n + 1];
    for (int x = st[top--]; ~top; x = st[top--])
        for (auto &[y, c] : g[x]) {
            d[y] = max(d[y], d[x] + c);
            if (--deg[y] == 0) st[++top] = y;
        }
    for (int i = 1; i <= n; ++i) ans += d[c[i]]; cout << ans;
    return 0;
}

⭐北大ACM队的远足

#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define pb push_back
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
using namespace std;
typedef long long ll;
typedef vector<int> VI;

const int N = 1e5 + 5, M = 2e5 + 5, mod = 999983;

int n, m, _, k;
int h[N], ne[M], to[M], co[M], tot;
int ph[N], pn[M], pt[M];
int indeg[N], outdeg[N], pre[N], st, ed, q;
ll fs[N], ft[N], dis[N];
bool cut[N], edge[M];
VI path;

int ds[N], dt[N], g[N << 1], d[N << 1];
bool bri[M];

void add(int u, int v, int c) {
    ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c;
    pn[tot] = ph[v]; pt[ph[v] = tot] = u;
}

void topsort(int s, int h[], int ne[], int to[], ll f[], int deg[]) {
    queue<int> q; f[s] = 1;
    rep (i, 1, n) if (!deg[i]) q.push(i);
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = h[x]; i; i = ne[i]) {
            int y = to[i], w = co[i];
            if (s == st && dis[y] > dis[x] + w) dis[y] = dis[x] + w, pre[y] = i;
            f[y] = (f[y] + f[x]) % mod;
            if (!--deg[y]) q.push(y);
        }
    }
}

void findedge() {
    rep(i, 1, tot) {
        int x = pt[i], y = to[i];
        edge[i] = fs[x] * ft[y] % mod == fs[ed];
    }
}

void getpath() {
    VI(1, ed).swap(path);
    for (int i = pre[ed]; to[i]; i = pre[pt[i]]) path.pb(pt[i]);
    reverse(all(path));
}

void solve() {
    getpath();
    per(i, path.size(), 1) d[i] = dis[path[i - 1]];
    rep(i, 2, path.size())
        bri[i] = edge[pre[path[i - 1]]], g[i] = g[i - 1] + (bri[i] ? co[pre[path[i - 1]]] : 0);
    int k = 1;
    rep(i, 2, path.size()) {
        ds[i] = min(g[i], ds[i - 1] + g[i] - g[i - 1]);
        while (k < i && d[i] - d[k] > q) ++k;
        ds[i] = min(ds[i], g[k] - (bri[k] ? q - d[i] + d[k] : 0));
    }
    dt[k = path.size()] = 0;
    per(i, path.size() - 1, 1) {
        dt[i] = min(g[path.size()] - g[i], dt[i + 1] + g[i + 1] - g[i]);
        while (k > i && d[k] - d[i] > q) --k;
        dt[i] = min(dt[i], g[path.size()] - g[k] - (bri[k + 1] ? q - d[k] + d[i] : 0));
    }
    int ans = 2e9;
    rep(i, 1, path.size()) ans = min(ans, ds[i] + dt[i]);
    k = 1;
    rep(i, 2, path.size()) {
        while (k < i && d[i] - d[k] > 2 * q) ++k;
        ans = min(ans, g[path.size()] - g[i] + g[k] - (bri[k] ? 2 * q - d[i] + d[k] : 0));
    }
    cout << ans << '\n';
}

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> m >> st >> ed >> q; tot = 0; ++st, ++ed;
        rep(i, 1, n) {
            ph[i] = h[i] = indeg[i] = outdeg[i] = pre[i] = 0;
            cut[i] = fs[i] = ft[i] = 0; dis[i] = 2e9;
        }
        dis[st] = 0;
        rep(i, 1, m) {
            int u, v, c; cin >> u >> v >> c;
            add(++u, ++v, c); ++indeg[v], ++outdeg[u];
        }
        topsort(st, h, ne, to, fs, indeg);
        if (dis[ed] == 2e9) { cout << -1 << '\n'; continue; }
        topsort(ed, ph, pn, pt, ft, outdeg);
        findedge(); solve();
    }
    return 0;
}

卡图难题

#include <bits/stdc++.h>
using namespace std;

const int N = 2e3 + 5, M = 1e6 + 5;

int n, m, _, k, h[N][N];
int dfn[N], low[N], df, st[N], top;
int c[N], scnt;
bool inst[N];
char s[5];

void tarjan(int x) {
    dfn[x] = low[x] = ++df, inst[st[++top] = x] = 1;
    for (int y = 1; y <= m; ++y) if (h[x][y])
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (inst[y]) low[x] = min(low[x], dfn[y]);
    if (low[x] == dfn[x]) {
        ++scnt; int y;
        do inst[y = st[top--]] = 0, c[y] = scnt;  while (y != x);
    }
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; ++i) {
        int a, b, c; cin >> a >> b >> c >> s;
        switch (s[0]) {
            case 'X' : if (c) h[a][b + n] = h[a + n][b] = h[b][a + n] = h[b + n][a] = 1;
                else h[a][b] = h[a + n][b + n] = h[b][a] = h[b + n][a + n] = 1; break;
            case 'O' : if (c) h[a][b + n] = h[b][a + n] = 1;
                else h[a][a + n] = h[b][b + n] = 1; break;
            case 'A' : if (c) h[a + n][a] = h[b + n][b] = 1;
                else h[a + n][b] = h[b + n][a] = 1; break;
        }
    } m = n << 1;
    for (int i = 1; i <= n; ++i) if (!dfn[i]) top = 0, tarjan(i);
    for (int i = 1; i <= n; ++i) if (c[i] == c[i + n]) return cout << "NO", 0;
    cout << "YES";
    return 0;
}

⭐牧师约翰最忙碌的一天

#include <bits/stdc++.h>
using namespace std;

const int N = 2e3 + 5;

int n, m, _, k, h[N][N];
int dfn[N], low[N], df, st[N], top;
int c[N], scnt, opp[N], val[N];
bool inst[N];
pair<int, int> t[N];

void tarjan(int x) {
    dfn[x] = low[x] = ++df, inst[st[++top] = x] = 1;
    for (int y = 1; y <= m; ++y) if (h[x][y])
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (inst[y]) low[x] = min(low[x], dfn[y]);
    if (low[x] == dfn[x]) {
        ++scnt; int y;
        do inst[y = st[top--]] = 0, c[y] = scnt; while (y != x);
    }
}

void _print(int c) {
    int h = c / 60, m = c % 60;
    if (h < 10) printf("0%d:", h);
    else printf("%d:", h);
    if (m < 10) printf("0%d", m);
    else printf("%d", m);
}

void print(int x) { _print(t[x].first); printf(" "); _print(t[x].second); puts(""); }

int main() {
    scanf("%d", &n); bool f = 1; m = n << 1;
    for (int i = 1; i <= n; ++i) {
        int a, b, c, d, e; opp[i] = i + n, opp[i + n] = i;
        scanf("%d:%d %d:%d %d", &a, &b, &c, &d, &e);
        t[i] = { a * 60 + b, a * 60 + b + e }; t[i + n] = { c * 60 + d - e, c * 60 + d };
        if (t[i].first > t[i].second || t[i + n].first > t[i + n].second) f = 0;
    }
    for (int i = 1; i < m; ++i) for (int j = i + 1; j <= m; ++j)
        if (i == j || j == i + n) continue;
        else if (t[i].second > t[j].first && t[i].first < t[j].second) h[i][opp[j]] = h[j][opp[i]] = 1;
    for (int i = 1; i <= m; ++i) if (!dfn[i]) top = 0, tarjan(i);
    for (int i = 1; i <= n; ++i) if (c[i] == c[i + n]) return puts("NO"), 0;
    puts("YES");
    for (int i = 1; i <= m; ++i) val[i] = c[i] > c[opp[i]];
    for (int i = 1; i <= n; ++i) print(val[i] ? opp[i] : i);
    return 0;
}

二分图的匹配

棋盘覆盖

bool dfs(int x, int y) {
    for (int i = 0; i < 4; ++i) {
        int nx = x + d[i][0], ny = y + d[i][1];
        if (max(nx, ny) > n || min(nx, ny) < 1 || v[nx][ny] || b[nx][ny]) continue;
        v[nx][ny] = 1;
        if (!a[nx][ny].first || dfs(a[nx][ny].first, a[nx][ny].second)) return a[nx][ny] = { x, y }, 1;
    }
    return 0;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; ++i) {
        int x, y; cin >> x >> y;
        b[x][y] = 1;
    }
    for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) if (!b[i][j] && i + j & 1)
        memset(v, 0, sizeof v), k += dfs(i, j);
    cout << k;
    return 0;
}

車的放置

#include <bits/stdc++.h>
using namespace std;

const int N = 2e2 + 5;

int n, m, k, match[N];
bool g[N][N], v[N];

bool dfs(int x) {
    for (int i = 1; i <= m; ++i) if (!g[x][i] && !v[i]) {
        v[i] = 1;
        if (!match[i] || dfs(match[i])) return match[i] = x, 1;
    }
    return 0;
}

int main() {
    cin >> n >> m;
    for (cin >> k; k; --k) {
        int x, y; cin >> x >> y;
        g[x][y] = 1;
    }
    for (int i = 1; i <= n; ++i) memset(v, 0, sizeof v), k += dfs(i);
    cout << k;
    return 0;
}

⭐导弹系统

double dis(int x, int y) { return sqr(a[x].first - b[y].first) + sqr(a[x].second - b[y].second); }

bool dfs(int x, double mid) {
    for (int i = n * c; i; --i) if (!v[i]) {
        if (dis(x, (i - 1) / c + 1) > sqr(sp) * sqr(mid - (i - 1) % c * (t1 + t2) - t1)) continue;
        v[i] = 1; if (!match[i] || dfs(match[i], mid)) { match[i] = x; return 1; } 
    }
    return 0;
}

bool check(double mid) {
    c = (int)min((mid + t2) / (t1 + t2), (double)m); memset(match, 0, sizeof match);
    for (int i = 1; i <= m; ++i) { memset(v, 0, sizeof v); if (!dfs(i, mid)) return 0; }
    return 1;
}

int main() {
    cin >> n >> m >> t1 >> t2 >> sp; t1 /= 60;
    for (int i = 1; i <= m; ++i) cin >> a[i].first >> a[i].second;
    for (int i = 1; i <= n; ++i) cin >> b[i].first >> b[i].second;
    double l = 0, r = 2e6;
    while (r - l >= 1e-7) {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    cout << setiosflags(ios::fixed) << setprecision(6) << r;
    return 0;
}

⭐蚂蚁

#include <bits/stdc++.h>
#define sqr(n) ((n)*(n))
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define per(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;

template<class T1, class T2> bool umin(T1& a, T2 b) { return a > b ? (a = b, true) : false; }
template<class T1, class T2> bool umax(T1& a, T2 b) { return a < b ? (a = b, true) : false; }

const int N = 1e2 + 5;

int n, match[N];
pair<int, int> a[N], b[N];
double la[N], lb[N], delta, w[N][N];
bool va[N], vb[N];

bool dfs(int x) {
    va[x] = 1;
    rep(y, 1, n) if (!vb[y])
        if (fabs(la[x] + lb[y] - w[x][y]) < 1e-6) {
            vb[y] = 1;
            if (!match[y] || dfs(match[y])) { match[y] = x; return 1; }
        }
        else umin(delta, la[x] + lb[y] - w[x][y]);
    return 0;
}
void KM() {
    rep(i, 1, n) {
        la[i] = -2e9; lb[i] = 0;
        rep(j, 1, n) umax(la[i], w[i][j]);
    }
    rep(i, 1, n)
        while (1) {
            rep(j, 1, n) va[j] = vb[j] = 0;/*;*/delta = 2e9; if (dfs(i)) break;
            rep (x, 1, n) if (va[x]) rep (y, 1, n) if (!vb[y]) delta = min(delta, la[x] + lb[y] - w[x][y]);
            rep(j, 1, n) la[j] -= va[j] ? delta : 0, lb[j] += vb[j] ? delta : 0;
        }
    rep(i, 1, n) cout << match[i] << '\n';
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> b[i].first >> b[i].second;
    for (int i = 1; i <= n; ++i) cin >> a[i].first >> a[i].second;
    for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j)
        w[i][j] = -sqrt(sqr(a[i].first - b[j].first) + sqr(a[i].second - b[j].second));
    KM();
    return 0;
}

二分图的覆盖与独立集

机器任务

int n, m, _, k, match[N], ans;
vector<int> h[N];
bool v[N];

bool dfs(int x) {
    for (auto &y : h[x]) if (!v[y]) {
        v[y] = 1;
        if (!match[y] || dfs(match[y])) return match[y] = x, 1;
    }
    return 0;
}

int main() {
    while (cin >> n, n) {
        cin >> m >> k; memset(match, 0, sizeof match); int ans = 0;
        for (int i = 1; i <= n + m; ++i) vector<int>().swap(h[i]);
        for (int i = 1; i <= k; ++i) {
            int u, v, c; cin >> c >> u >> v;
            if (u && v) h[u].push_back(v + n);
        }
        for (int i = 1; i <= n; ++i) memset(v, 0, sizeof v), ans += dfs(i);
        cout << ans << '\n';
    }
    return 0;
}

⭐泥泞的区域

int n, m, _, k;
vector<int> h[N * N];
int match[N * N], ans;
int a[N][N], b[N][N], cnt1, cnt2;
bool v[N * N];
char s[N][N];

bool dfs(int x) {
    for (auto &y : h[x]) if (!v[y]) {
        v[y] = 1;
        if (!match[y] || dfs(match[y])) return match[y] = x;
    }
    return 0;
}

int main() {
    cin >> n >> m;
    rep (i, 1, n) rep (j, 1, m) {
        cin >> s[i][j];
        if (s[i][j] == '.') continue;
        if (s[i][j - 1] == '*') a[i][j] = a[i][j - 1];
        else a[i][j] = ++cnt1;
        if (s[i - 1][j] == '*') b[i][j] = b[i - 1][j];
        else b[i][j] = ++cnt2;
    }
    rep (i, 1, n) rep (j, 1, m) h[a[i][j]].push_back(b[i][j]); 
    rep (i, 1, cnt1) memset(v, 0, sizeof v), ans +=  dfs(i);
    cout << ans;
    return 0;
}

⭐骑士放置

棋盘匹配问题一般将 编号奇数作为左点, 偶数为右点

int n, m, _, k;
vector<int> h[N * N]; 
int match[N * N], ans;
bool v[N * N], a[N][N];

bool dfs(int x) {
    for (auto &y : h[x]) if (!v[y]) {
        v[y] = 1;
        if (!match[y] || dfs(match[y])) return match[y] = x;
    }
    return 0;
}

int get(int x, int y) { return (x - 1) * m + y; }

bool check(int x, int y) { return (min(x, y) > 0 && x <= n && y <= m && !a[x][y]); }

int main() {
    cin >> n >> m >> k;
    rep (i, 1, k) {
        int x, y; cin >> x >> y;
        a[x][y] = 1;
    }
    rep (i, 1, n) rep (j, 1, m) if (!a[i][j]) {
        int id = get(i, j);
        if (check(i + 1, j + 2)) h[id].push_back(get(i + 1, j + 2)), h[get(i + 1, j + 2)].push_back(id);
        if (check(i + 2, j + 1)) h[id].push_back(get(i + 2, j + 1)), h[get(i + 2, j + 1)].push_back(id);
        if (check(i - 1, j + 2)) h[id].push_back(get(i - 1, j + 2)), h[get(i - 1, j + 2)].push_back(id);
        if (check(i - 2, j + 1)) h[id].push_back(get(i - 2, j + 1)), h[get(i - 2, j + 1)].push_back(id);
    }
    rep (i, 1, n) rep (j, 1, m) if (!a[i][j] && i + j & 1) memset(v, 0, sizeof v), ans += dfs(get(i, j));
    cout << n * m - k - ans;
    return 0;
}

⭐捉迷藏

选择的藏身点, 即dfs(i) 失败的的i点

int n, m, _, k, match[N];
bool h[N][N], v[N];

bool dfs(int x) {
    rep (y, 1, n) if (h[x][y] && !v[y]) {
        v[y] = 1;
        if (!match[y] || dfs(match[y])) return match[y] = x, 1;
    }
    return 0;
}

int main() {
    cin >> n >> m; int ans = n;
    rep (i, 1, m) {
        int u, v; cin >> u >> v;
        h[u][v] = 1;
    }
    rep (k, 1, n) rep (i, 1, n) rep (j, 1, n) h[i][j] |= h[i][k] & h[k][j];
    rep (i, 1, n) memset(v, 0, sizeof v), ans -= dfs(i);
    cout << ans;
    return 0;
}

网络流初步

⭐舞动的夜晚

对于有向边边 1~m

co[i << 1 | 1] 无残余流量且链接不同连通分量为无用边

co[i << 1] 无残余流量且链接不相同连通分量为必要边

其他边为可行边

int n, m, _, k;
int h[N], ne[M], to[M], co[M], id[M], now[N], tot;
int d[N], s, t, c[N], scnt;
int dfn[N], low[N], df, st[N], top;
bool inst[N];
int indeg[N], outdeg[N];

void add(int u, int v, int c, int idx) {
    ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c; id[tot] = idx;
    ne[++tot] = h[v]; to[h[v] = tot] = u; co[tot] = 0; id[tot] = idx;
}

bool bfs() {
    memset(d, 0, sizeof d); memcpy(now, h, sizeof h);
    queue<int> q; q.push(s); d[s] = 1;
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = h[x]; i; i = ne[i]) {
            if (!co[i]) continue;
            int y = to[i];
            if (d[y]) continue;
            d[y] = d[x] + 1; q.push(y);
            if (y == t) return 1;
        }
    }
    return 0;
}

int dinic(int x, int flow) {
    if (x == t) return flow;
    int rest = flow, k;
    for (int &i = now[x]; i && rest; i = ne[i]) {
        if (!co[i]) continue;
        int y = to[i];
        if (d[y] != d[x] + 1) continue;
        k = dinic(y, min(rest, co[i]));
        if (!k) d[y] = 0;
        co[i] -= k, co[i ^ 1] += k; rest -= k;
    }
    return flow - rest;
}

void tarjan(int x) {
    dfn[x] = low[x] = ++df; inst[st[++top] = x] = 1;
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (co[i])
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (inst[y]) low[x] = min(low[x], dfn[y]);
    if (low[x] == dfn[x]) {
        ++scnt; int y;
        do inst[y = st[top--]] = 0, c[y] = scnt; while (y != x);
    }
}

int main() {
    IOS; cin >> n >> m >> k; vector<int> ans;
    tot = 1; s = n + m + 1, t = s + 1;
    rep (i, 1, k) {
        int u, v; cin >> u >> v;
        add(u, v + n, 1, i);
    }
    rep (i, 1, n) add(s, i, 1, 0);
    rep (i, 1, m) add(i + n, t, 1, 0);
    while (bfs()) while (dinic(s, inf));
    rep (i, 1, n + m + 2) if (!dfn[i]) top = 0, tarjan(i);
    rep (i, 1, k) if (!co[i << 1 | 1]) {
        int x = to[i << 1 | 1], y = to[i << 1];
        if (c[x] != c[y]) ans.push_back(id[i << 1]);
    }
    cout << ans.size() << '\n';
    for (auto &i : ans) cout << i << ' ';
    cout << '\n';
    return 0;
}

⭐有线电视网络

点化边之后枚举s和t, 而不是人为添加s 和 t

int n, m, _, k;
int h[N], ne[M], to[M], co[M], now[N], tot;
int d[N], s, t, maxflow, e[M];

void add(int u, int v, int c) {
    ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c;
    ne[++tot] = h[v]; to[h[v] = tot] = u; co[tot] = 0;
}

bool bfs() {
    rep(i, 1, m) d[i] = 0, now[i] = h[i];
    queue<int> q; q.push(s); d[s] = 1;
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (co[i] && !d[y]) {
            d[y] = d[x] + 1; q.push(y);
            if (y == t) return 1;
        }
    }
    return 0;
}

int dinic(int x, int flow) {
    if (x == t) return flow;
    int rest = flow, k;
    for (int &i = now[x], y = to[i]; i && rest; y = to[i = ne[i]]) if (co[i] && d[y] == d[x] + 1)
        if (!(k = dinic(y, min(rest, co[i])))) d[y] = 0;
        else co[i] -= k, co[i ^ 1] += k, rest -= k;
    return flow - rest;
}

int main() {
    while (~scanf("%d%d", &n, &m)) {
        tot = 1; k = n;
        rep(i, 1, n) h[i] = h[n + i] = 0, add(i, i + n, 1);
        rep(i, 1, m) {
            int u, v; scanf(" (%d,%d)", &u, &v);
            add(++u + n, ++v, inf); add(v + n, u, inf);
        } m = n << 1;
        rep (i, 2, tot) e[i] = co[i];
        for (s = n + 1; s <= m; ++s) for (t = s - n + 1; t <= n; ++t) {
            int flow = maxflow = 0;
            rep (i, 2, tot) co[i] = e[i];
            while (bfs()) while (flow = dinic(s, inf)) maxflow += flow;
            k = min(k, maxflow);
        }
        printf("%d\n", k);
    }
    return 0;
}

k取方格数

const int N = 5e3 + 5, M = 4e4 + 5, inf = 1 << 30;
int n, m, _, k;
int h[N], to[M], ne[M], co[M], ed[M], tot;
int v[N], incf[N], pre[N], s, t, maxflow, d[N], ans;
int a[N][N];
void add(int u, int v, int e, int c) {
    ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c, ed[tot] = e;
    ne[++tot] = h[v]; to[h[v] = tot] = u; co[tot] = -c; ed[tot] = 0;
}
bool bfs() {
    rep (i, 1, n) v[i] = 0, d[i] = -inf;
    queue<int> q; q.push(s); v[s] = 1; d[s] = 0;
    incf[s] = inf; //增广路上各边的最小剩余容量
    while (!q.empty()) {
        int x = q.front(); q.pop(); v[x] = 0;
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (ed[i] && d[y] < d[x] + co[i]) {
            d[y] = d[x] + co[i]; pre[y] = i;
            incf[y] = min(incf[x], ed[i]);
            if (!v[y]) q.push(y), v[y] = 1;
        }
    }
    return d[t] != -inf;
}
void update() {
    for (int x = t, i = pre[x]; x != s; i = pre[x = to[i ^ 1]])
        ed[i] -= incf[t], ed[i ^ 1] += incf[t];
    maxflow += incf[t]; ans += d[t];
}

int get(int x, int y) { return n * (x - 1) + y; }

int main() {
    IOS; cin >> n >> k; tot = 1; s = 1, t = n * n * 2;
    rep (i, 1, n) rep (j, 1, n) {
        cin >> m, add(get(i, j), get(i, j) + n * n, 1, m);
        add(get(i, j), get(i, j) + n * n, k - 1, 0);
        if (i - 1) add(get(i - 1, j) + n * n, get(i, j), k, 0);
        if (j - 1) add(get(i, j - 1) + n * n, get(i, j), k, 0);
    }
    n = n * n * 2;
    while (bfs()) update(); cout << ans;
    return 0;
}

总结与练习

⭐观光

注意更新最小值的时候, 最小值也要更新次小值

int n, m, _, k, ans;
int d[2][N], cnt[2][N], s, t, v[N];
vector<vector<PII>> h, g;

void dij(int s) {
    priority_queue<PII> q; q.push({ 0, s }); memset(v, 0, sizeof v);
    d[0][s] = 0, cnt[0][s] = 1;
    while (!q.empty()) {
        int x = q.top().se; q.pop();
        if (v[x]) continue; v[x] = 1;
        for (auto& [y, c] : h[x]) {
            if (d[0][y] > d[0][x] + c) {
                if (umin(d[1][y], d[0][y])) cnt[1][y] = cnt[0][y];
                q.push({ -(d[0][y] = d[0][x] + c), y }), cnt[0][y] = cnt[0][x];
            }
            else if (d[0][y] == d[0][x] + c) cnt[0][y] += cnt[0][x];
            if (d[0][y] == d[0][x] + c) continue;
            if (umin(d[1][y], d[0][x] + c)) cnt[1][y] = cnt[0][x];
            else if (d[1][y] == d[0][x] + c) cnt[1][y] += cnt[0][x];
        }
    }
    rep(i, 1, n)
        if (d[1][i] != d[0][i] + 1) cnt[1][i] = 0, d[1][i] = d[0][i] + 1;
        else q.push({ -d[1][i], i });
    while (!q.empty()) {
        int x = q.top().se; q.pop();
        if (v[x] == 2) continue; v[x] = 2;
        for (auto& [y, c] : h[x]) if (d[1][y] == d[1][x] + c)
            cnt[1][y] += cnt[1][x], q.push({ -d[1][y], y });
    }
}

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> m; vector<vector<PII>>(n + 1).swap(h); ans = 0;
        rep(i, 1, m) { int u, v, c; cin >> u >> v >> c; h[u].pb(v, c); }
        cin >> s >> t; memset(d, 0x3f, sizeof d); memset(cnt, 0, sizeof cnt);
        dij(s); cout << cnt[1][t] + cnt[0][t] << '\n';
    }
    return 0;
}

升降电梯上

int n, m, _, k;
vector<pair<int, int>> h[N];
int d[N], s, t, a[21];
bool v[N];

void dji(int s) {
    priority_queue<pair<int, int>> q;
    memset(d, 0x3f, sizeof d); q.push({ d[s] = 0, s });
    while (!q.empty()) {
        int x = q.top().second; q.pop();
        if (v[x]) continue; v[x] = 1;
        for (auto &[y, c] : h[x]) if (d[y] > d[x] + c) q.push({-(d[y] = d[x] + c), y});
    }
}

int main() {
    cin >> n >> m;
    rep (i, 1, m) { cin >> a[i]; if (!a[i]) k = i; }
    memset(d, 0x3f, sizeof d);
    rep (i, 1, n) rep(j, 1, m) rep(t, 1, m) if (a[t] && i + a[t] > 0 && i + a[t] <= n)
        h[(i - 1) * m + j].emplace_back((i + a[t] - 1) * m + t, abs(a[t]) * 2 + abs(j - t));
    dji(k);
    int ans = d[0];
    rep(i, (n - 1) * m + 1, n * m) ans = min(ans, d[i]);
    cout << (ans == d[0]? - 1 : ans);
    return 0;
}

GF和猫咪的玩具

int main() {
    cin >> n >> m; memset(d, 0x3f, sizeof d);
    rep (i, 0, n) d[i][i] = 0;
    rep (i, 1, m) {
        int u, v; cin >> u >> v;
        d[u][v] = d[v][u] = min(d[u][v], 1);    
    }
    rep (k, 1, n) rep (i, 1, n) rep (j, 1, n)
        d[i][j] = min(d[i][k] + d[k][j], d[i][j]);
    rep (i, 1, n) rep (j, 1, n) d[0][0] = max(d[0][0], d[i][j]);
    cout << d[0][0]; 
    return 0;
}

社交网络

int main() {
    cin >> n >> m; memset(d, 0x3f, sizeof d);
    rep (i, 1, n) d[i][i] = 0;
    rep (i, 1, m) {
        int u, v, c; cin >> u >> v >> c;
        if (d[u][v] > c) d[u][v] = d[v][u] = c, a[u][v] = a[v][u] = 1;
        else if (d[u][v] == c) ++a[u][v], ++a[v][u];
    }
    rep (k, 1, n) rep (i, 1, n) rep (j, 1, n) if (j != k && i != k)
        if (d[i][j] > d[i][k] + d[k][j]) d[i][j] = d[i][k] + d[k][j], a[i][j] = a[i][k] * a[k][j];
        else if (d[i][j] == d[i][k] + d[k][j]) a[i][j] += a[i][k] * a[k][j];
    rep (k, 1, n) rep (i, 1, n) rep (j, 1, n)  if (i != k && j != k && a[i][j])
        if (d[i][k] + d[k][j] == d[i][j]) c[k] += (double)a[i][k] * a[k][j] / a[i][j];
    rep (i, 1, n) cout << setiosflags(ios::fixed) << setprecision(3) << c[i] << '\n';
    return 0;
}

北极网络

int ff(int x) { return x == f[x] ? x : f[x] = ff(f[x]); }

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> m; int k = 0;
        for (int i = 1; i <= m; ++i) cin >> a[i].first >> a[i].second;
        for (int i = 1; i < m; ++i) for (int j = 1; j <= m; ++j) d[i][j] = d[j][i] = 1e9;
        for (int i = 1; i < m; ++i) for (int j = i + 1; j <= m; ++j)
            d[i][j] = d[j][i] = sqrt(sqr(a[i].first - a[j].first) + sqr(a[i].second - a[j].second));
        for (int k = 1; k <= m; ++k) for (int i = 1; i <= m; ++i) for (int j = 1; j <= m; ++j)
            d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
        for (int i = 1; i < m; ++i) for (int j = i + 1; j <= m; ++j) e[++k] = { i, j, d[i][j] };
        sort(e + 1, e + k + 1, [](node& a, node& b) { return a.c < b.c; });
        for (int i = 1; i <= m; ++i) f[i] = i;
        for (int i = 1; i <= k; ++i) {
            int x = ff(e[i].x), y = ff(e[i].y);
            if (x == y) continue; --m; f[y] = x;
            if (m == n) { cout << setiosflags(ios::fixed) << setprecision(2) << e[i].c << '\n'; break; }
        }
    }
    return 0;
}

⭐四叶草魔杖

int cul(int d, int c) {
    int ans = 0;
    for (int i = 0; i < n; ++i) f[i] = i;
    for (int i = 0; i < m && c; ++i) {
        int x = ff(e[i].x), y = ff(e[i].y);
        if (x == y || !(d >> x & 1) || !(d >> y & 1)) continue;
        --c, f[y] = x, ans += e[i].c;
    }
    return c ? 0x3f3f3f3f : ans;
}

int main() {
    cin >> n >> m; memset(ans, 0x3f, sizeof ans);
    for (int i = 0; i < n; ++i) cin >> a[i];
    for (int i = 0; i < m; ++i) cin >> e[i].x >> e[i].y >> e[i].c;
    sort(e, e + m, [](node& a, node& b) { return a.c < b.c; });
    for (int i = 1; i < 1 << n; ++i) {
        int s = 0, c = 0; bool f = 0;
        for (int j = 0; j < n; ++j) if (i >> j & 1) ++c, s += a[j], f = f | s;
        if (!f) ans[i] = 0;
        else if (s == 0)
            if ((ans[i] = cul(i, c - 1)) == 0x3f3f3f3f) continue;
            else for (int j = 1; j <= i >> 1; ++j) if ((i | j) == i)
                ans[i] = min(ans[i], ans[i ^ j] + ans[j]);
    }
    if (ans[(1 << n) - 1] == 0x3f3f3f3f) cout << "Impossible";
    else cout << ans[(1 << n) - 1];
    return 0;
}

⭐直径

int n, m, cnt, las;
vector<pair<int, int>> h[N];
long long d[N], ans;

int dfs(int x, int fa, int dep) {
    int res = dep;
    for (auto& [y, c] : h[x]) if (y != fa) {
        int cur = dfs(y, x, dep + 1);
        if (ans < d[x] + d[y] + c)
            ans = d[x] + d[y] + c, las = dep, cnt = res + cur - dep * 2;
        else if (ans == d[x] + d[y] + c) {
            if (d[y] + c >= d[x]) cnt = cur - las; 
            else cnt = res - las;
        }
        if (d[x] < d[y] + c) d[x] = d[y] + c, res = cur;
        else if (d[x] == d[y] + c) res = dep;
    }
    return res;
}

int main() {
    IOS; cin >> n;
    for (int i = 1; i < n; ++i) {
        int u, v, c; cin >> u >> v >> c;
        h[u].emplace_back(v, c); h[v].emplace_back(u, c);
    }
    dfs(1, 0, 1); cout << ans << '\n' << cnt;
    return 0;
}

逃课的小孩

void dfs(int u, int fa) {
    for (int i = h[u], y = to[i]; i; y = to[i = ne[i]]) if (y ^ fa) {
        f[y] = u; d[y] = d[u] + co[i]; dfs(y, u);
        if (d[0] < d[y]) d[0] = d[y], f[0] = y;
    }
}

void work(int& p, int& q) {
    d[0] = -1; d[1] = 0; dfs(1, 0); p = f[0];
    d[0] = -1; d[p] = 0; dfs(p, 0); q = f[0];
}

void dfss(int u, int fa) {
    for (int i = h[u], y = to[i]; i; y = to[i = ne[i]]) if (y != fa) {
        dfss(y, u);
        fd[u] = max(fd[u], fd[y] + co[i]);
        if (!st.count(u)) continue;
        ans = max(ans, min(d[u], d[q] - d[u]));
        if (st.count(y)) continue;
        ans = max(ans, fd[y] + co[i] + min(d[u], d[q] - d[u]));
    }
}

int main() {
    IOS; cin >> n >> m;
    rep (i, 1, m) {
        int u, v, c; cin >> u >> v >> c;
        add(u, v, c); add(v, u, c);
    }
    work(p, q); st.insert(p);
    for (int i = q; i != p; st.insert(i), i = f[i]);
    dfss(p, 0); cout << ans + d[q];
    return 0;
}

聚会

const int N = 5e5 + 5;

struct STFrom {
    int f[N][20], dep[N], lg[N], t;//N为节点的数量
    vector<int> *h;
    void init(int n, vector<int>* H) {
        t = log2(n - 1) + 1; h = H; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1;
        rep(i, 0, t) f[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (auto &y : h[x]) {
                if (dep[y]) continue;
                dep[y] = dep[x] + 1; f[y][0] = x; q.push(y);
                for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
            }
        }
    }
    int lca(int x, int y) {
        if (dep[x] > dep[y]) swap(x, y);
        for (int k = dep[y] - dep[x]; ~lg[k]; k ^= 1 << lg[k]) y = f[y][lg[k]];
        if (x == y) return x;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    int dist(int x, int y) { return dep[x] + dep[y] - (dep[lca(x, y)]<<1); }
} ST;

int n, m;
vector<int> h[N];

int main() {
    IOS; cin >> n >> m;
    rep (i, 2, n) {
        int u, v; cin >> u >> v;
        h[u].push_back(v); h[v].push_back(u);
    }
    ST.init(n, h); ST.bfs(1);
    rep (i, 1, m) {
        int x, y, z, xy, xz, yz, lc, p; cin >> x >> y >> z;
        xy= ST.lca(x, y), xz = ST.lca(x, z), yz = ST.lca(y, z), lc = ST.lca(xy, z);
        int ans = 1e9, d = ST.dep[x] + ST.dep[y] + ST.dep[z] - ST.dep[lc] * 2;
        if (umin(ans, d - ST.dep[xy])) p = xy;
        if (umin(ans, d - ST.dep[xz])) p = xz;
        if (umin(ans, d - ST.dep[yz])) p = yz;a
        cout << p << ' ' << ans << '\n';
    }
    return 0;
}

会合

struct STFrom {
    int f[N][20], dep[N], lg[N], t;//N为节点的数量
    int *h, *ne, *to, *fa;
    bool *v;
    void init(int n, int* H, int* Ne, int* T, int* F, bool* V) {
        t = log2(n - 1) + 1; h = H, ne = Ne, to = T, fa = F, v = V; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1; fa[s] = s;
        rep(i, 0, t) f[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (!v[y] && !dep[y]) {
                fa[y] = s; dep[y] = dep[x] + 1; f[y][0] = x; q.push(y);
                for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
            }
        }
    }
    int lca(int x, int y) {
        if (dep[x] > dep[y]) swap(x, y);
        for (int k = dep[y] - dep[x]; ~lg[k]; k ^= 1 << lg[k]) y = f[y][lg[k]];
        if (x == y) return x;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
} ST;

int n, m, _, k, cas;
int h[N], ne[N << 1], to[N << 1], tot;
int id[N], fa[N], st[N], top, d[N], cir[N];
bool v[N], vis[N];

void add(int u, int v) { ne[++tot] = h[u]; to[h[u] = tot] = v; }

int dfs(int x, int e) {
    int s = 0;
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (i ^ e)
        if (!vis[y]) vis[y] = 1, id[y] = id[x], s = s ^ dfs(y, i ^ 1);
        else st[++top] = y, s = i & 1 ? y : -y;
    if (s) st[++top] = x, v[x] = 1;
    if (abs(s) == x) {
        if (s == x) rep (i, 2, top) d[st[i]] = d[st[i - 1]] + 1, ST.bfs(st[i]);
        else if (s == -x) per (i, top - 1, 1) d[st[i]] = d[st[i + 1]] + 1, ST.bfs(st[i]);
        return cir[id[x]] = d[x], d[x] = 0;
    }
    return s;
}

int main() {
    IOS; cin >> n >> m; tot = 1;
    rep (i, 2, n) { int a; cin >> a; add(i, a); add(a, i); }
    ST.init(n, h, ne, to, fa, v);
    rep (i, 1, n) if (!vis[i]) vis[i] = 1, id[i] = i, top = 0, dfs(i, 0);
    rep (i, 1, m) {
        int x, y; cin >> x >> y;
        if (id[x] ^ id[y]) cout << "-1 -1\n";
        else if (fa[x] == fa[y]) {
            int lc = ST.dep[ST.lca(x, y)];
            cout << ST.dep[x] - lc << ' ' << ST.dep[y] - lc << '\n';
        } else {
            int a = ST.dep[x] - 1, b = ST.dep[y] - 1; x = fa[x], y = fa[y];
            int c = (d[y] - d[x] + cir[id[x]]) % cir[id[x]], d = cir[id[x]] - c;
            if (max(a + c, b) < max(a, b + d)) cout << a + c << ' ' << b << '\n';
            else if (max(a + c, b) > max(a, b + d) || min(a + c, b) > min(a, b + d))
                cout << a << ' ' << b + d << '\n';
            else cout << a + c << ' ' << b << '\n';
        }
    }
    return 0;
}

⭐雇佣收银员

看题目, 又是一堆不等式约束, 就想差分约束

老套路 设s[i] 表示 [0, i] 时间段内正在工作和已经工作完的总人数,

考虑到, 差分约束, 要从个入点开始, 也就是-1,

所以我们右移, 将 0~23 小时变为 1~24 小时, 0为入点且s[0] ≡ 0;

然而这是个环, 我们要拆环, 如果从 昨天工作到今天凌晨的不算入 s[1],

所以对 每小时的工作人数, 可这样获得(每个人工作8小时)

i >= 8, s[i] - s[i- 8] >= R[i - 1] ps:时间被我们右移了
i <= 7, s[i] + s[24] - s[24 - 8 + i] >= R[i - 1]

我们要将所有节点(小时)连接起来, 设b[i], 表示 在第i小时(平移过的)有多少人可以开始工作

s[i] - s[i - 1] >= 0
s[i] - s[i - 1] <= b[i]
s[24] - s[0] <= n
s[24] - s[0] >= 0

整理得

s[0] = 0
s[i] >= s[i- 1]
s[i - 1] >= s[i] - b[i]
i >= 8, s[i] >= s[i - 8] + R[i - 1]
i <= 7, s[i] >= s[16 + i] - s[24] + R[i - 1]
s[0] >= s[24] - n
s[24] >= s[0] 

问题来了, (1)对于 i<= 7 的不等式, 怎么和两个点有关

再仔细一看, s[24]正是我们所求, 且s[24]是个与i无关的数, 即"常数"

所以我们可以枚举 s[24] 为 s24, 然后使得 s[24] - s[0] <= s24, s[24] - s[0] >= s24, 强制约束

由于求最小值, 从小到大枚举即可,

思考的, s[24]是单调的, 那么就可以二分

然后就是建边问题, 我们按照

s[i] >= s[i- 1]
s[i - 1] >= s[i] - b[i]
i >= 8, s[i] >= s[i - 8] + R[i - 1]

i <= 7, s[i] >= s[16 + i] -+ R[i - 1]
s[0] >= s[24]
s[24] >= s[0]

对于 边的序号 > 65的边 在搜索时取花费时额外 减去当前枚举的 s24, 并且 左后一条边的花费预先 加上 s24 * 2(取的时候 -s24)

int n, m, _, k;
int h[N], ne[3 * N], to[3 * N], co[3 * N], tot;
int a[N], b[N], dis[N], dep[N];
bool v[N];

void add(int u, int v, int c) { ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c; }

bool sfpa(int mid) {
    memset(v, 0, sizeof v); memset(dis, 0xcf, sizeof dis);
    stack<int> st; st.push(dep[0] = dis[0] = 0);
    while (!st.empty()) {
        int x = st.top(); st.pop(); v[x] = 0;
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) {
            int c = co[i] - (i > 65) * mid;
            if (dis[y] >= dis[x] + c) continue;
            dis[y] = dis[x] + c; dep[y] = dep[x] + 1;
            if (dep[y] >= 25) return 0;
            if (!v[y]) v[y] = 1, st.push(y);
        }
    }
    return 1;
}

int main() {
    IOS;
    for (cin >> _; _; --_) {
        rep (i, 1, 24) cin >> a[i]; cin >> n; tot = 0;
        memset(h, 0, sizeof h); memset(b, 0, sizeof b);
        rep (i, 1, n) cin >> m, ++b[++m];
        rep (i, 1, 24) add(i, i - 1, -b[i]), add(i - 1, i, 0);
        rep (i, 8, 24) add(i - 8, i, a[i]);
        rep (i, 1, 7) add(i + 16, i, a[i]);
        add (24, 0, 0); add(0, 24, 0);
        int l = 0, r = n + 1;
        while (l < r) {
            int mid = l + r >> 1; co[tot] = mid << 1; 
            if (sfpa(mid)) r = mid;
            else l = mid + 1;
        }
        if (r == n + 1) cout << "No Solution\n";
        else cout << r << '\n';
    }
    return 0;
}

⭐最优高铁环

二分不解释, 注意有向图遍历负环环的技巧

int n, m, _, k, f[N], dep[N];
vector<pair<int, int>> h[N];
unordered_map<ull, int> st;
double dis[N];
bool v[N];

int get(int x) {
    switch (x) {
    case 'S': return 1000;
    case 'G': return 500;
    case 'D': return 300;
    case 'T': return 200;
    default: return 150;
    }
}

bool dfs(int x, double mid) {
    v[x] = 1;
    for (auto &[y, c] : h[x]) {
        if (dis[y] > dis[x] + mid - c) {
            dis[y] = dis[x] + mid - c;
            if (v[y] || dfs(y, mid)) return 1;
        }
    }
    return v[x] = 0;
}

bool check(double mid) {
    rep (i, 1, n) dis[i] = 2e4, v[i] = 0;
    rep (i, 1, n) if(dfs(i, mid)) return 1;
    return 0;
}

int main() {
    IOS; cin >> m;
    rep(i, 1, m) {
        string s; cin >> s; ull u = 0, v = 0; int co = 0;
        for (char& c : s)
            if (c == '-') u = (u ? u : v), v = 0;
            else co += (v ? 0 : get(c)), v = v * p + c;
        if (!st.count(u)) st[u] = st.size() + 1;
        if (!st.count(v)) st[v] = st.size() + 1;
        h[st[u]].emplace_back(st[v], co);
    } n = st.size();
    double l = 0, r = 2e4, ans = -1;
    while (r - l > 1e-2) {
        double mid = (r + l) / 2;
        if (check(mid)) l = ans = mid;
        else r = mid;
    }
    cout << round(ans);
    return 0;
}

冗余路径

int n, m, _, k;
int h[N], to[M << 1], ne[M << 1], tot;
int dfn[N], low[N], df, st[N], top;
int ecc[N], ecnt, deg[N];

void add(int u, int v) {
    ne[++tot] = h[u]; to[h[u] = tot] = v;
}

void tarjan(int x, int e) {
    dfn[x] = low[x] = ++df; st[++top] = x;
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (i ^ e)
        if (!dfn[y]) tarjan(y, i ^ 1), low[x] = min(low[x], low[y]);
        else low[x] = min(low[x], dfn[y]);
    if (low[x] == dfn[x]) {
        ++ecnt; int y;
        do ecc[y = st[top--]] = ecnt; while (y ^ x);
    }
}

int main() {
    IOS; cin >> n >> m; tot = 1;
    rep (i, 1, m) {
        int u, v; cin >> u >> v;
        add(u, v); add(v, u);
    }
    rep (i, 1, n) if (!dfn[i]) top = 0, tarjan(i, 0);
    for (int i = 2; i <= tot; i += 2) {
        int x = to[i ^ 1], y = to[i];
        if (ecc[x] == ecc[y]) continue;
        ++deg[ecc[x]], ++deg[ecc[y]];
    }
    rep (i, 1, ecnt) if (deg[i] == 1) ++k;
    cout << (k + 1) / 2;
    return 0;
}

⭐矿场搭建

对于缩完点的一颗树,

对于单点, 只能在这个点建, 这个点塌了, 正好

对于单缩点, 任意选两个点, 一个点塌了, 还能到另一个

对于树形结构, 塌一个割点, 将树分成两部分

对于dcc连接多个dcc(有多个割点), 不管塌哪个割点, 都能从其他的割点逃离

对于只有一个割点的, 选dcc内除了割点的任意一点, 割点塌了, 能直达, 这个点塌了, 从割点逃离

int n, m, _, k;
vector<int> h[N];
int dfn[N], low[N], df, st[N], top, root;
vector<vector<int>> dcc;
bool cut[N];

void tarjan(int x) {
    dfn[x] = low[x] = ++df;
    if (h[x].empty()) { dcc.push_back(vector<int>(1, x)); return; }
    st[++top] = x; int cnt = 0;
    for (auto &y : h[x])
        if (!dfn[y]) {
            tarjan(y); low[x] = min(low[x], low[y]);
            if (dfn[x] <= low[y]) {
                ++cnt; dcc.push_back(vector<int>(1, x)); int z;
                if (x != root || cnt > 1) cut[x] = 1;
                do dcc.back().push_back(z = st[top--]); while (z ^ y);
            }
        }
        else low[x] = min(low[x], dfn[y]);
}

int main() {
    IOS;
    while (cin >> m, n = df = 0, m) {
        rep(i, 1, m) {
            int u, v; cin >> u >> v;
            if (u > v) swap(u, v);
            while (n < v) ++n, vector<int>().swap(h[n]), dfn[n] = cut[n] = 0;
            h[u].push_back(v); h[v].push_back(u);
        }
        int ans = 0; long long sum = 1;
        rep(i, 1, n) if (!dfn[i]) {
            vector<vector<int>>().swap(dcc); tarjan(root = i);
            if (dcc.size() == 1)
                if (dcc[0].size() == 1) ans += 1;
                else ans += 2, sum *= dcc[0].size() * (dcc[0].size() - 1) >> 1;
            else for (auto& ve : dcc) {
                int cnt = 0;
                for (auto& i : ve) if (cut[i]) ++cnt;
                if (cnt == 1) ++ans, sum *= ve.size() - 1;
            }
        }
        cout << "Case " << ++_ << ": " << ans << ' ' << sum << '\n';
    }
    return 0;
}

逃不掉的路

void add(int u, int v) { ne[++tot] = h[u]; to[h[u] = tot] = v; }

void add_c(int u, int v) { nc[++totc] = hc[u]; tc[hc[u] = totc] = v; }

void bfs(int s) {
    queue<int> q; q.push(s); dep[s] = 1;
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = hc[x], y = tc[i]; i; y = tc[i = nc[i]]) if (!dep[y]) {
            dep[y] = dep[x] + 1; f[y][0] = x;
            for (int j = 1; j <= t; ++j)  f[y][j] = f[f[y][j - 1]][j - 1];
            q.push(y);
        } 
    }
}

int lca(int x, int y) {
    if (dep[x] > dep[y]) swap(x, y);
    per (i, t, 0) if (dep[f[y][i]] >= dep[x]) y = f[y][i];
    if (x == y) return x;
    per (i, t, 0) if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
    return f[x][0]; 
}

int dist(int x, int y) { return dep[x] + dep[y] - 2 * dep[lca(x, y)]; }

void tarjan(int x, int bian) {
    dfn[x] = low[x] = ++df;
    st[++top] = x;
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]])
        if (!dfn[y]) {
            tarjan(y, i); low[x] = min(low[x], low[y]);
            if (dfn[x] < low[y]) edge[i] = edge[i ^ 1] = 1;
        }
        else if (i != (bian ^ 1)) low[x] = min(low[x], dfn[y]);
    if (low[x] == dfn[x]) {
        ++ecnt; int y;
        do ecc[y = st[top--]] = ecnt; while (y ^ x);
    }
}

int main() {
    IOS; cin >> n >> m; tot = 1;
    rep (i, 1, m) {
        int u, v; cin >> u >> v;
        add(u, v); add(v, u);
    }
    rep (i, 1, n) if (!dfn[i]) top = 0, tarjan(i, 0);
    rep (i, 2, tot) { //可能有重边
        int x = to[i ^ 1], y = to[i];
        if (ecc[x] ^ ecc[y]) add_c(ecc[x], ecc[y]);
    }
    t = log2(ecnt - 1) + 1; bfs(1); cin >> m;
    rep (i, 1, m) {
        int u, v; cin >> u >> v; u = ecc[u], v = ecc[v];
        cout << dist(u, v) << '\n';
    }
    return 0;
}

⭐交通实时查询系统

const int N = 1e4 + 5, M = 2e5 + 5;

struct STFrom {
    static const int N = 2e4 + 5;
    int f[N][20], dep[N], lg[N], t;//N为节点的数量
    int* h, * ne, * to;
    void init(int n, int* H, int* Ne, int* To) {
        t = log2(n - 1) + 1; h = H, ne = Ne, to = To; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1;
        rep(i, 0, t) f[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) {
                if (dep[y]) continue;
                dep[y] = dep[x] + 1; f[y][0] = x; q.push(y);
                for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
            }
        }
    }
    int lca(int x, int y) {
        if (dep[x] > dep[y]) swap(x, y);
        for (int k = dep[y] - dep[x]; ~lg[k]; k ^= 1 << lg[k]) y = f[y][lg[k]];
        if (x == y) return x;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    int dist(int x, int y) { return dep[x] + dep[y] - (dep[lca(x, y)] << 1); }
} ST;

int n, m, _, q;
int h[N], ne[M], to[M], tot, bl[M >> 1];
int dfn[N], low[N], df, st[N], top, c[N];
vector<vector<int>> vcc;
bool cut[N], root;
int newid[N << 1];
int nh[N << 1], nne[N << 2], nto[M << 2], ntot;

void add(int u, int v) { ne[++tot] = h[u]; to[h[u] = tot] = v; }

void add_c(int u, int v) { nne[++ntot] = nh[u]; nto[nh[u] = ntot] = v; }

void tarjan(int x) {
    dfn[x] = low[x] = ++df, st[++top] = x; int cnt = 0;
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]])
        if (!dfn[y]) {
            tarjan(y); low[x] = min(low[x], low[y]);
            if (dfn[x] <= low[y]) {
                vcc.push_back(vector<int>(1, x));
                if (++cnt > 1 || x != root) cut[x] = 1;
                for (int z = 0; z ^ y; z = st[top--]) vcc.back().push_back(st[top]);
            }
        }
        else low[x] = min(low[x], dfn[y]);
}

int main() {
    IOS;
    while (cin >> n >> m, n || m) {
        vcc.resize(0);
        rep(i, 1, n) h[i] = dfn[i] = cut[i] = newid[i] = 0;
        df = ntot = 0; tot = 1;
        rep(i, 1, m) {
            int u, v; cin >> u >> v;
            add(u, v); add(v, u);
        }
        rep(i, 1, n) if (!dfn[i]) top = 0, root = i, tarjan(i);
        int num = vcc.size();
        rep (i, 1, n) if (cut[i]) newid[i] = ++num;
        rep(i, 1, num) nh[i] = 0;
        rep(i, 1, vcc.size())
            for (auto& x : vcc[i - 1]) {
                c[x] = i;
                if (cut[x]) add_c(newid[x], i), add_c(i, newid[x]);
                for (int j = h[x]; j; j = ne[j]) if (c[to[j]] == i) bl[j >> 1] = i;
            }
        ST.init(num, nh, nne, nto);
        rep(i, 1, num) if (!ST.dep[i]) ST.bfs(i);
        for (cin >> q; q; --q) {
            int x, y; cin >> x >> y; x = bl[x], y = bl[y];
            cout << (ST.dist(x, y) + 1 >> 1) << '\n';
        }
    }
    return 0;
}

约翰的旅行

bool eulerc(int s) {
    for (auto& i : edge)
        if (i.size() & 1) return 0;
        else sort(all(i), greater<PII>());
    top = t = 0; st[++top] = 0; st[++top] = s;
    while (top) {
        int x = st[top];
        while (!edge[x].empty() && vis[edge[x].back().fi]) edge[x].pop_back();
        if (!edge[x].empty()) {
            st[++top] = edge[x].back().fi;
            st[++top] = edge[x].back().se;
            vis[st[top - 1]] = 1; edge[x].pop_back();
        }
        else ans[++t] = st[(--top)--];
    }
    return 1;
}

int main() {
    while (1) {
        edge.resize(0); k = n = 0;
        while (cin >> u >> v, u && v) {
            if (u > v) swap(u, v);
            if (!n) n = u;
            cin >> z; ++k; vis[z] = 0;
            per(i, v - edge.size() + 1, 1) edge.pb(vector<PII>(0));
            edge[u].pb(z, v); edge[v].pb(z, u);
        }
        if (!n) break;
        if (eulerc(n)) per(i, t - 1, 1) cout << ans[i] << ' ';
        else cout << "Round trip does not exist.";
        cout << '\n';
    }
    return 0;
}

⭐太鼓达人

注意到最多有\(2^k\)个01串, 构造是不太可能的

代码不难, 转换图的思想却很难想到(不放专题里, 我是想不到转换图做)

注意到任何一个串可以通向两个另外的串, 也由两个串可以到达, 故每个串的入度和出度都为2

注意到这\(2^k\)串组成的图是个欧拉图, 那么必定存在欧拉回路, 之后就是怎么走路径最小了

int n, m, _, k, a, ans[2050];
bool v[2050];

bool dfs(int x, int k) {
    if (k == (1 << n)) return 1; v[x] = 1;
    if (!v [(x << 1) & a]) if (dfs((x << 1) & a, k + 1)) return ans[k] = 0, 1;
    if (!v[(x << 1 | 1) & a]) if (dfs((x << 1 | 1) & a, k + 1)) return ans[k] = 1;
    return v[x] = 0;
}

int main() {
    cin >> n; a = 1 << n;
    cout << a-- << ' '; dfs(0, 1);
    for (int i = 1; i <= n; ++i) cout << 0;
    for (int i = 1; i <= a - n + 1; ++i) cout << ans[i];
    return 0;
}

⭐从u到v还是从v到u?

void tarjan(int x) {
    dfn[x] = low[x] = ++df; inst[st[++top] = x] = 1;
    for (auto& y : h[x])
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (inst[y]) low[x] = min(low[x], dfn[y]);
    if (low[x] == dfn[x]) {
        ++scnt;
        for (int y = 0; y ^ x; y = st[top--]) c[st[top]] = scnt, inst[st[top]] = 0;
    }
}

int main() {
    for (cin >> _; _; --_) {
        cin >> n >> m; df = scnt = top = 0;
        for (int i = 1; i <= n; ++i) h[i].resize(0), dfn[i] = inst[i] = 0;
        for (int i = 1; i <= m; ++i) {
            int u, v; cin >> u >> v;
            h[u].push_back(v); ++deg[v];
        }
        int f = 0, id = 1; for (int i = 1; i <= n; ++i) if (!deg[i]) ++f, id = i; else deg[i] = 0;
        if (f > 1) { cout << "No\n"; continue; } tarjan(id); id = f = 0;
        vector<set<int>> g(scnt + 1);
        for (int x = 1; x <= n; ++x) for (auto& y : h[x]) 
            if ((c[y] ^ c[x]) && !g[c[x]].count(c[y])) g[c[x]].insert(c[y]), ++deg[c[y]];
        for (int i = 1; i <= scnt; ++i) if (!deg[i]) ++f, id = i;
        if (f > 1) { cout << "No\n"; continue; } f = 0;
        st[top = 1] = id;
        while (top) {
            int x = st[top--];
            for (auto& y : g[x]) if (--deg[y] == 0) st[++top] = y;
            if (top > 1) f = 1, top = 0;
        }
        for (int i = 1; i <= scnt; deg[i++] = 0) if (deg[i] > 1) ++f;
        cout << (f ? "No\n" : "Yes\n");
    }
    return 0;
}

⭐杀人游戏

考虑孤点

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
using namespace std;

const int N = 1e5 + 5, M = 3e5 + 5;

int n, m, _, k;
vector<int> h[N], g[N];
int dfn[N], low[N], df, st[N], top;
int c[N], scnt;
vector<vector<int>> scc;
bool inst[N], f;
int indeg[N];

void tarjan(int x) {
    dfn[x] = low[x] = ++df; inst[st[++top] = x] = 1;
    for (auto &y : h[x])
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (inst[y]) low[x] = min(low[x], dfn[y]);
    if (low[x] == dfn[x]) {
        ++scnt; int y; scc.push_back(vector<int>());
        do inst[y = st[top--]] = 0, c[y] = scnt, scc.back().push_back(y); while (y ^ x);
    }
}

int dfs(int x) {
    int ans = scc[x - 1].size();
    for (auto &y : g[x]) ans += dfs(y);
    return ans;
}

int main() {
    IOS; cin >> n >> m;
    rep (i, 1, m) {
        int u, v; cin >> u >> v;
        h[u].push_back(v);
    }
    rep (i, 1, n) if (!dfn[i]) top = 0, tarjan(i);
    rep (i, 1, n) for (auto &y : h[i])
        if (c[i] ^ c[y]) g[c[i]].push_back(c[y]), ++indeg[c[y]];
    int x = 0;
    rep (i, 1, scnt) if (!indeg[i]) {
        ++x;
        if (scc[i - 1].size() > 1 || f) continue;
        f = 1;
        for (auto &y : g[i]) if (indeg[y] == 1) f = 0;
    }
    double res = ((double)n - x + f) / n;
    cout << setiosflags(ios::fixed) << setprecision(6) << res;
    return 0;
}

⭐平面

平面图:设无向图G,若能将G画在一个平面上,使得任何两条边仅在顶点处相交,则称G是具有平面性质的图,简称平面图,否则称G是非平面图。

在平面图G中,G的边将其所在的平面划分成的区域称为面,有限的区域称为有限面或内部面,无线的区域称为无限面或外部面,
包围面的边称为该面的边界,包围每个面的所有边组成的回路长度称为该面的次数(桥计算两次)。

那么有, 平面图G所有面的次数之和等于边数的两倍(类似于握手定理)。

欧拉定理: 对于一个简单通平面图, 设G是一个面数为 f 的(n,m)平面图,则 n-m+f=2.(归纳法证)

推论1, 设G是一个面数为 f 的(n,m)平面图,且有p个连通分支,则 n-m+f=p+1(所有连通分支共用一个无限面)

推论2, 假设G是一个面数为 f 的(n,m)连通简单平面图,n≥3,每个面的次数至少是 p(p≥3),则m≤(n-2)*p/(p-2)。//n>=3, 则边数 m>=2,无限面的次数至少为2*2
      有定义出发, 面的次数之和等于边数两倍, 故 f*p ≤ 2 * m, 带入欧拉公式可得上式
int n, m, _, k;
PII e[M];
int f[M << 1], id[N];

int ff(int x) { return x == f[x] ? x : f[x] = ff(f[x]); }

bool check(int a, int b, int c) {
    if(id[a] > id[b]) swap(a, b);
    return id[a] < id[c] && id[b] > id[c];
}

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> m;
        rep (i, 1, m) cin >> e[i].fi >> e[i].se, f[i] = i, f[i + m] = i + m;
        rep (i, 1, n) cin >> k, id[k] = i;
        if (m > 3 * n - 6) { cout << "NO\n"; continue; }
        rep (i, 1, m) rep (j, i + 1, m) {
            if (e[i].fi == e[j].fi || e[i].fi == e[j].se || e[i].se == e[j].fi || e[i].se == e[j].se) continue;
            if (check(e[i].fi, e[i].se, e[j].fi) == check(e[i].fi, e[i].se, e[j].se)) continue;
            f[ff(i)] = ff(j + m), f[ff(j)] = ff(i + m);
        }
        bool f = 1;
        rep (i, 1, m) if (ff(i) == ff(i + m)) { f = 0; break; }
        cout << (f ? "YES\n" : "NO\n");
    }
    return 0;
}

⭐婚礼

按照题意, 分为新郎 和 新娘列, 然而新郎列出了gay佬, (新郎不介意出现百合)

此问题就是如何决策新郎那列座位问题

即新郎那列\(a_i\)这个位置坐第i对的新郎还是新娘的问题

\(a_i=0\)代表这个位置坐新娘, 反之为新郎,

那直接并查集?

然而不行, 并查集解决的是无向的取值问题,

而这题关系是单向的(新娘列不关注有无通奸者)

那就只能2-sat了,

而这题比较特殊, 新郎列第0个位置必须为新郎(强制排除了一些答案)

根据2-sat逆序拓扑取值问题, 我们人为加边 (0, n-1) 强制 n-1 在 1拓扑序的后面

最后求完一组可行解, 是新郎列的, 我们还要把性别取反, 变成新娘列

void tarjan(int x) {
    dfn[x] = low[x] = ++df; inst[st[++top] = x] = 1;
    rep(y, 1, k) if (h[x][y])
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (inst[y]) low[x] = min(low[x], dfn[y]);
    if (low[x] == dfn[x]) {
        ++scnt; int y;
        do inst[y = st[top--]] = 0, c[y] = scnt; while (y != x);
    }
}

bool check() {
    rep(i, 1, n) if (c[i] == c[i + n]) return puts("bad luck"), 1;
    return 0;
}

int main() {
    while (~scanf("%d%d", &n, &m), n || m) {
        k = n << 1; scnt = df = 0;
        rep(i, 1, n) opp[i] = i + n, opp[i + n] = i;
        rep(i, 1, k) dfn[i] = inst[i] = 0;
        rep(i, 1, k) rep(j, 1, k) h[i][j] = 0;
        h[1][n + 1] = 1;
        rep(i, 1, m) {
            int a, b; char s, t; scanf("%d%c %d%c", &a, &s, &b, &t);
            if (s == 'h') a += n;
            if (t == 'h') b += n;
            ++a, ++b, h[a][opp[b]] = h[b][opp[a]] = 1;
        }
        rep(i, 1, k) if (!dfn[i]) top = 0, tarjan(i);
        if (check()) continue;
        rep(i, 2, n) printf("%d%c ", i - 1, c[i] > c[i + n] ? 'w' : 'h'); puts("");
    }
    return 0;
}

⭐将他们分好队

并查集还要再去判断并查集内部是否不矛盾, 然后不同祖先的两个集合也要去判断, 集合内部是否矛盾

直接染色, 去跑图

将图每次分成3部分, 必须和i同时在一个组的, 必须和i不能再同一个组的, 和自由选择的

保证和i同组的能随便选自由的吗不和i同组也可选自由的, 直接跑dp即可

int n, m, _, k;
int h[N][N], v[N], f[N][N << 1];
VI a, b;
vector<pair<VI, VI>> ans;

bool dfs(int x, int c) {
    if (c == 1) v[x] = 1, ans.back().fi.pb(x);
    else v[x] = 2, ans.back().se.pb(x);
    rep(y, 1, n) if (h[x][y] && v[y] != 3 - c)
        if (v[y] == c || dfs(y, 3 - c)) return 1;
    return 0;
}

void work(int n, int k) {
    if (!n) return;
    int c = ans[n - 1].fi.size() - ans[n - 1].se.size();
    if (f[n][k] == 1) {
        a.insert(a.end(), all(ans[n - 1].fi));
        b.insert(b.end(), all(ans[n - 1].se));
        work(n - 1, k - c);
    } else {
        a.insert(a.end(), all(ans[n - 1].se));
        b.insert(b.end(), all(ans[n - 1].fi));
        work(n - 1, k + c);
    }
}

int main() {
    IOS; cin >> n;
    rep(i, 1, n) rep(j, i + 1, n) h[i][j] = h[j][i] = 1;
    rep(i, 1, n) while (cin >> m, m) h[i][m] = 0;
    rep(i, 1, n) rep(j, 1, n) if (h[i][j]) h[j][i] = 1;
    rep(i, 1, n) if (!v[i]) {
        ans.pb({ VI(), VI() });
        if (dfs(i, 1)) return cout << "No solution", 0;
    }
    memset(f, -1, sizeof f); f[0][n] = 0;
    rep(i, 1, ans.size()) rep(j, 0, n << 1) {
        int c = ans[i - 1].fi.size() - ans[i - 1].se.size();
        if (f[i - 1][j] != -1) f[i][j + c] = 1, f[i][j - c] = 2;
    }
    rep(j, 0, n)
        if (f[ans.size()][j + n] != -1) { work(ans.size(), j + n); break; }
        else if (f[ans.size()][n - j] != -1) { work(ans.size(), n - j); break; }
    sort(all(a)); sort(all(b));
    cout << a.size(); for (auto& i : a) cout << ' ' << i;
    cout << '\n' << b.size(); for (auto& i : b) cout << ' ' << i;
    return 0;
}

放置机器人

bool dfs(int x) {
    for (auto &y : h[x]) {
        if (v[y]) continue; v[y] = 1;
        if (!match[y] || dfs(match[y])) return match[y] = x, 1;
    }
    return 0;
}

int main() {
    IOS; int cas = 0;
    for (cin >> _; _; --_) {
        cin >> n >> m; h.resize(1); match.resize(1);
        rep (i, 1, n) {
            cin >> s[i] + 1; match.pb(0);
            rep (j, 1, m)
                if (s[i][j] == '#') match.pb(0);
                else idx[i][j] = match.size() - 1;
        }
        rep (j, 1, m)  {
            h.pb(VI());
            rep (i, 1, n)
                if (s[i][j] == '#') h.pb(VI());
                else if (s[i][j] == 'o') h.back().pb(idx[i][j]);
        }
        int ans = 0;
        rep (i, 1, h.size() - 1) { vector<bool>(match.size()).swap(v); ans += dfs(i); }
        cout << "Case :" << ++cas << '\n' << ans << '\n';
    }
    return 0;
}

稳定的牛分配

bool dfs(int x, int l, int r) {
    rep(i, l, r) if (cnt[ls[x][i]] < g[ls[x][i]]) return match[ls[x][i]][++cnt[ls[x][i]]] = x, 1;
    rep (i, l, r) rep (j, 1, g[ls[x][i]]) if (!v[ls[x][i]][j]) {
        v[ls[x][i]][j] = 1;
        rep (j, 1, g[ls[x][i]]) if (dfs(match[ls[x][i]][j], l, r)) return match[ls[x][i]][j] = x, 1; 
    }
    return 0;
}

int main() {
    cin >> n >> m; k = inf;
    rep (i, 1, n) rep (j, 1, m) cin >> ls[i][j];
    rep (i, 1, m) cin >> g[i];
    for (int l = 1, r = 1; l <= m; ++l) for (; r <= m; ++r) {
        rep (i, 1, m) cnt[i] = 0; bool f = 1;
        rep (i, 1, n) { memset(v, 0, sizeof v); if (!(f = dfs(i, l, r))) { f = 0; break; } }
        if (f) { k = min(k, r - l + 1); break; }
    }
    cout << k;
    return 0;
}

回家

int n, m, _, k;
int h[N], to[M], ne[M], co[M], ed[M], tot;
int v[N], incf[N], pre[N], s, t, maxflow, d[N], ans;
vector<PII> person, house;

void add(int u, int v, int e, int c) {
    ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c, ed[tot] = e;
    ne[++tot] = h[v]; to[h[v] = tot] = u; co[tot] = -c; ed[tot] = 0; 
}

bool bfs() {
    rep (i, 1, n) v[i] = 0, d[i] = inf;
    queue<int> q; q.push(s); v[s] = 1; d[s] = 0;
    incf[s] = inf; //增广路上各边的最小剩余容量
    while (!q.empty()) {
        int x = q.front(); q.pop(); v[x] = 0;
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) {
            if (!ed[i] || d[y] <= d[x] + co[i]) continue;
            d[y] = d[x] + co[i]; pre[y] = i;
            incf[y] = min(incf[x], ed[i]);
            if (!v[y]) q.push(y), v[y] = 1;
        }
    }
    return d[t] != inf;
}

void update() {
    for (int x = t, i = pre[x]; x != s; i = pre[x = to[i ^ 1]])
        ed[i] -= incf[t], ed[i ^ 1] += incf[t];
    maxflow += incf[t]; ans += d[t] * incf[t];
}

int dis(int x, int y) {
    return abs(person[x].fi - house[y].fi) + abs(person[x].se - house[y].se); 
}

void init() {
    clear(person); clear(house);
    rep (i, 1, n) rep (j, 1, m) {
        char c; cin >> c;
        if (c == 'm') person.pb({ i, j });
        else if (c == 'H') house.pb({ i, j });
    }
    n = person.size() + house.size();
    s = ++n; t = ++n; tot = 1; maxflow = ans = 0;
    rep (i, 1, n) h[i] = 0;
    rep(i, 0, person.size() - 1) rep(j, 0, house.size() - 1)
        add(i + 1, j + 1 + person.size(), 1, dis(i, j));
    rep (i, 0, person.size() - 1) add(s, i + 1, 1, 0);
    rep (i, 0, house.size() - 1) add(person.size() + i + 1, t, 1, 0);
}

int main() {
    IOS;
    while (cin >> n >> m, n || m) {
        init(); while (bfs()) update();
        cout << ans << '\n';
    }
    return 0;
}

空袭

void add(int u, int v) { ne[++tot] = h[u]; to[h[u] = tot] = v; }

bool dfs(int x) {
    for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (!v[y]) {
        v[y] = 1;
        if (!match[y] || dfs(match[y])) return match[y] = x;
    }
    return 0;
}

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> m; tot = 2;
        rep (i, 1, n) match[i] = h[i] = 0;
        rep (i, 1, m) { int u, v; cin >> u >> v; add(u, v); }
        ans = n;
        rep (i, 1, n) { rep (i, 1, n) v[i] = 0; ans -= dfs(i); }
        cout << ans << '\n';
    }
    return 0;
}

排版幻灯片

int n, m, _, k;
vector<int> h[N];
int id[N >> 1], ans, deg[N];
bool v[N >> 1];
pair<PII, PII> paper[N >> 1];

bool check(int i, int x, int y) {
    return x >= paper[i].fi.fi && x <= paper[i].fi.se && y >= paper[i].se.fi && y <= paper[i].se.se;
}

int main() {
    IOS;
    while (cin >> n, m = 0, n) {
        rep(i, 1, n) h[i].resize(0), h[i + n].resize(0), id[i] = deg[i] = deg[i + n] = v[i + n] = 0;
        rep(i, 1, n) cin >> paper[i].fi.fi >> paper[i].fi.se >> paper[i].se.fi >> paper[i].se.se;
        rep(i, 1, n) {
            int x, y; cin >> x >> y;
            rep(j, 1, n) if (check(j, x, y)) h[i + n].push_back(j), h[j].push_back(i + n), ++deg[j], ++deg[i + n];
        }
        while (1) {
            int c = 0;
            rep(i, 1, n << 1) if (deg[i] == 1) { c = i; break; }
            if (!c) break; --deg[c]; ++m;
            if (c <= n) for (auto &y : h[c]) { if (!v[y]) { id[c] = y - n, v[c = y] = 1; break; } }
            else for (auto &y : h[c]) if (!id[y]) { id[y] = c - n, v[c = y] = 1; break; }
            for (auto &y : h[c]) --deg[y];
        }
        cout << "Heap " << ++_ << '\n';
        if (m) rep(i, 1, n) { if (id[i]) cout << '(' << char('A' + i - 1) << ',' << id[i] << ") "; }
        else cout << "none"; cout << '\n' << '\n';
    }
    return 0;
}

国王的任务

int n, m, _, k;
vector<int> h[N];
int dfn[N], low[N], df, st[N], top;
int c[N], scnt;
bool inst[N];

void tarjan(int x) {
    dfn[x] = low[x] = ++df; inst[st[++top] = x] = 1;
    for (auto &y : h[x])
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (inst[y]) low[x] = min(low[x], dfn[y]);
    if (low[x] == dfn[x]) {
        ++scnt; int y;
        do inst[y = st[top--]] = 0, c[y] = scnt; while (y != x);
    }
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) for (cin >> _; _; --_) cin >> m, h[i].emplace_back(m + n);
    for (int i = 1; i <= n; ++i) cin >> m, h[m + n].emplace_back(i);
    for (int i = 1; i <= n; ++i) if (!dfn[i]) top = 0, tarjan(i);
    for (int i = 1; i <= n; ++i) {
        vector<int> ans;
        for (auto &y : h[i]) if (c[i] == c[y]) ans.emplace_back(y - n);
        sort(ans.begin(), ans.end()); cout << ans.size();
        for (auto &j : ans) cout << ' ' << j; cout << '\n';
    }
    return 0;
}

排水沟

int n, m, _, k;
int h[N], ne[M], to[M], co[M], now[N], tot;
int d[N], s, t, maxflow;

void add(int u, int v, int c) {
    ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c;
    ne[++tot] = h[v]; to[h[v] = tot] = u; co[tot] = 0;
}

bool bfs() {
    memset(d, 0, sizeof d); memcpy(now, h, sizeof h);
    queue<int> q; q.push(s); d[s] = 1;
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (co[i] && !d[y]) {
            d[y] = d[x] + 1; q.push(y);
            if (y == t) return 1;
        }
    }
    return 0;
}

int dinic(int x, int flow) {
    if (x == t) return flow;
    int rest = flow, k;
    for (int &i = now[x], y = to[i]; i && rest; y = to[i = ne[i]]) if (co[i] && d[y] == d[x] + 1)
        if (!(k = dinic(y, min(rest, co[i])))) d[y] = 0;
        else co[i] -= k, co[i ^ 1] += k, rest -= k;
    return flow - rest;
}

int main() {
    IOS; cin >> m >> n; tot = 1; s = 1, t = n;
    rep (i, 1, m) {
        int u, v, c; cin >> u >> v >> c;
        add(u, v, c);
    }
    int flow = 0;
    while (bfs()) while (flow = dinic(s, inf)) maxflow += flow;
    cout << maxflow;
    return 0;
}
posted @ 2021-01-09 01:10  洛绫璃  阅读(311)  评论(0编辑  收藏  举报