Say 题选记(9.14 - 9.20)

P6619 [省选联考 2020 A/B 卷] 冰火战士

树状数组倍增板子。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 5;
#define lowbit(i) ((i) & (-(i)))
int a[2][N], n, _x[N], cnt, sum[2];
void add(int a[], int x, int k){
    for(int i = x; i <= cnt; i += lowbit(i))
        a[i] += k;
}
int query(int a[], int x){
    int sum = 0;
    for(int i = x; i; i -= lowbit(i)) sum += a[i];
    return sum;
}
struct node{
    int op, idx, x, y;
}Op[N];
int main(){
    cin.tie(nullptr)->sync_with_stdio(0);
    cin >> n;
    for(int i = 1; i <= n; ++i){
        cin >> Op[i].op >> Op[i].idx;
        if(Op[i].op == 1){
            cin >> Op[i].x >> Op[i].y;
            _x[++cnt] = Op[i].x;
        }
        else{
            Op[i].op = -1;
            int idx = Op[i].idx;
            Op[i].x = Op[idx].x;
            Op[i].y = Op[idx].y;
            Op[i].idx = Op[idx].idx;
        }
    }
    sort(_x + 1, _x + 1 + cnt);
    cnt = unique(_x + 1, _x + 1 + cnt) - _x - 1;
    for(int i = 1; i <= n; ++i){
        Op[i].x = lower_bound(_x + 1, _x + 1 + cnt, Op[i].x) - _x;
        int x = Op[i].x, y = Op[i].y, op = Op[i].op, idx = Op[i].idx;
        add(a[idx], x + idx, op * y);
        sum[idx] += op * y;
        int nw0 = 0, nw1 = sum[1], p = 0;
        for(int j = 20; j >= 0; --j){
            int nxtp = p + (1 << j);
            if(nxtp <= cnt && nw0 + a[0][nxtp] <= nw1 - a[1][nxtp]){
                nw0 += a[0][nxtp], nw1 -= a[1][nxtp];
                p = nxtp;
            }
        }
        int ans1 = nw0, ans2 = 0, p2 = 0;
        if(p < cnt){
            ans2 = sum[1] - query(a[1], p + 1);
            int nw = sum[1];
            for(int j = 20; j >= 0; --j){
                int nxtp = p2 + (1 << j);
                if(nxtp <= cnt && nw - a[1][nxtp] >= ans2){
                    nw -= a[1][nxtp], p2 = nxtp;
                }
            }
        }
        if(max(ans1, ans2) == 0) cout << "Peace\n";
        else{
            if(ans1 > ans2) cout << _x[p] << ' ' << 2 * ans1 << '\n';
            else cout << _x[p2] << ' ' << 2 * ans2 << '\n';
        }
    }
    return 0;
}

P4768 [NOI2018] 归程

Kruskal 重构树板子,注意求的是最大最小瓶颈路还是最小最大瓶颈路(可能这么叫?

Code
using namespace std;
typedef pair<int, int> pii;
const int N = 4e5 + 5;
struct edge{
    int u, v, w, f, pre;
}e[N * 2];
int n, m, head[N], fa[N], cnt, dis[N], val[N], mn[N], st[N][21];
vector<int> g[N];
void adde(int u, int v, int w, int f){
    e[++cnt] = {u, v, w, f, head[u]};
    head[u] = cnt;
}
void dij(){
    priority_queue<pii, vector<pii>, greater<pii> > q;
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    q.emplace(0, 1);
    while(!q.empty()){
        auto [dist, u] = q.top();
        q.pop();
        if(dist > dis[u]) continue;
        for(int k = head[u]; k; k = e[k].pre){
            int v = e[k].v, w = e[k].w;
            if(dist + w < dis[v]){
                dis[v] = dist + w;
                q.emplace(dis[v], v);
            }
        }
    }
}
int getf(int u){ return u == fa[u] ? u : fa[u] = getf(fa[u]); }
void dfs(int u){
    mn[u] = dis[u];
    for(int i = 1; i <= 19; ++i) st[u][i] = st[st[u][i - 1]][i - 1];
    for(int v : g[u]){
        st[v][0] = u;
        dfs(v);
        mn[u] = min(mn[v], mn[u]);
    }
}
int main(){
    cin.tie(nullptr)->sync_with_stdio(0);
    int T;
    cin >> T;
    while(T--){
        memset(head, 0, sizeof(head));
        memset(g, 0, sizeof(g));
        memset(mn, 0x3f, sizeof(mn));
        cnt = 0;
        cin >> n >> m;
        for(int i = 1; i <= m; ++i){
            int u, v, w, f;
            cin >> u >> v >> w >> f;
            adde(u, v, w, f), adde(v, u, w, f);
        }
        dij();
        // for(int i = 1; i <= n; ++i) cout << dis[i] << '\n';
        for(int i = 1; i <= 2 * n; ++i) fa[i] = i;
        sort(e + 1, e + 1 + cnt, [](edge x, edge y){
            return x.f > y.f;
        });
        int tot = n;
        for(int i = 1; i <= cnt; ++i){
            int u = e[i].u, v = e[i].v, f = e[i].f;
            int fu = getf(u), fv = getf(v);
            if(fu != fv){
                fa[fu] = fa[fv] = ++tot;
                g[tot].emplace_back(fu);
                g[tot].emplace_back(fv);
                val[tot] = f;
            }
            if(tot - n == n - 1) break;
        }
        st[tot][0] = 0;
        dfs(tot);
        int lst = 0, q, k, s;
        cin >> q >> k >> s;
        while(q--){
            int v, p;
            cin >> v >> p;
            v = (v + lst * k - 1) % n + 1;
            p = (p + lst * k) % (s + 1);
            for(int j = 19; j >= 0; --j){
                if(val[st[v][j]] > p) v = st[v][j];
            }
            cout << (lst = mn[v]) << '\n';
        }
    }
    return 0;
}

P9870 [NOIP2023] 双序列拓展

dp 很 trival,然后变成了一个网格图上有障碍点查询联通性的问题。
主要的思想就是总是考虑限制最强的地方,神来之笔,把 \(y_j\) 套上一个 \(\min\) 之后居然就有单调性了,然后就可以双指针做。
感觉第二篇题解是更自然的思路,但写的时候看的是第一篇题解(好像就是把双指针倒过来写了)。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 5;
typedef array<int, N> arr;
arr a, b, pamn, pbmn, pamx, pbmx, samn, samx, sbmn, sbmx, ta, tb;
bool check1(const arr&a, const arr &b, int x, int y){
    if(x == 1 || y == 1) return 1;
    if(a[pamn[x - 1]] < b[pbmn[y - 1]]) return check1(a, b, pamn[x - 1], y);
    else if(a[pamx[x - 1]] < b[pbmx[y - 1]]) return check1(a, b, x, pbmx[y - 1]);
    return 0;
}
bool check2(const arr&a, const arr &b, int x, int y, int enx, int eny){
    if(x == enx || y == eny) return 1;
    if(a[samn[x + 1]] < b[sbmn[y + 1]]) return check2(a, b, samn[x + 1], y, enx, eny);
    else if(a[samx[x + 1]] < b[sbmx[y + 1]]) return check2(a, b, x, sbmx[y + 1], enx, eny);
    return 0;
}
bool solve(const arr &a, const arr &b, int n, int m){
    auto get = [](const arr &x, int nw, int i, int fl){
        if(x[nw] * fl > x[i] * fl) return i;
        return nw;
    };
    
    pamn[1] = pamx[1] = pbmn[1] = pbmx[1] = 1;
    for(int i = 2; i <= n; ++i) pamn[i] = get(a, pamn[i - 1], i, 1), pamx[i] = get(a, pamx[i - 1], i, -1);
    for(int i = 2; i <= m; ++i) pbmn[i] = get(b, pbmn[i - 1], i, 1), pbmx[i] = get(b, pbmx[i - 1], i, -1);
    samn[n] = samx[n] = n, sbmn[m] = sbmx[m] = m;
    for(int i = n - 1; i >= 1; --i) samn[i] = get(a, samn[i + 1], i, 1), samx[i] = get(a, samx[i + 1], i, -1);
    for(int i = m - 1; i >= 1; --i) sbmn[i] = get(b, sbmn[i + 1], i, 1), sbmx[i] = get(b, sbmx[i + 1], i, -1);

    // for(int i = 1; i <= n; ++i) cout << pamn[i] << ' ' << samn[i] << ' ' << pamx[i] << ' ' << samx[i] << '\n';
    // for(int i = 1; i <= m; ++i) cout << pbmn[i] << ' ' << sbmn[i] << ' ' << pbmx[i] << ' ' << sbmx[i] << '\n';

    if(a[pamx[n]] >= b[pbmx[m]] || a[pamn[n]] >= b[pbmn[m]]) return 0;
    return check1(a, b, pamn[n], pbmx[m]) && check2(a, b, pamn[n], pbmx[m], n, m);
}
int main(){
    cin.tie(nullptr)->sync_with_stdio(0);
    int c, q, n, m;
    cin >> c >> n >> m >> q;
    for(int i = 1; i <= n; ++i) cin >> a[i];
    for(int i = 1; i <= m; ++i) cin >> b[i];
    cout << (solve(a, b, n, m) || solve(b, a, m, n) ? '1' : '0');
    while(q--){
        int kx, ky;
        cin >> kx >> ky;
        ta = a, tb = b;
        while(kx--){
            int p, v; cin >> p >> v;
            ta[p] = v;
        }
        while(ky--){
            int p, v; cin >> p >> v;
            tb[p] = v;
        }
        // for(int i = 1; i <= n; ++i) cout << ta[i] << ' ';
        // for(int j = 1; j <= m; ++j) cout << tb[j] << ' ';
        // cout << '\n';
        cout << (solve(ta, tb, n, m) || solve(tb, ta, m, n) ? '1' : '0');
    }
    return 0;
}

P7737 [NOI2021] 庆典

挺综合的一道题。
一开始看到这个 "对于三座城市 \(x\)\(y\)\(z\),若 \(x\Rightarrow z\)\(y\Rightarrow z\),那么有 \(x\Rightarrow y\)\(y\Rightarrow x\)" 性质的时候很疑惑,作用是在缩成 DAG 之后还可以进一步缩成树,注意下。
树上拓扑序就好处理多了,限制可以轻松地表示为在不在 \(u\) 的子树内。
考虑询问,发现暴力每次都跑一遍 dfs 求可不可达很亏,实际上只需要用建虚树的办法把关键点找出来建图跑就行,这样跑一次就是 \(O(k)\) 的了。
建虚树本身就是很有用的过程。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5;
int n, m, q, k;
vector<int> g[N], e[N], tmp[N];
int dfn[N], low[N], scc[N], cnt, tsp, st[N], tp, f[N][21], lg[N], siz[N], a[N], r, dis[N];
bitset<N> in, vis, vise, bk;
struct edge{
    int u, v, w, pre;
}s[N], _s[N];
int sh[N], tot, _sh[N], _tot;
struct node{
    int u, v;
}add[5];
void adde(int u, int v, int w){
    s[++tot] = {u, v, w, sh[u]};
    sh[u] = tot;
    _s[++_tot] = {v, u, w, _sh[v]};
    _sh[v] = _tot;
    vise[tot] = 0;
}

void tarjan(int u){
    dfn[u] = low[u] = ++tsp;
    in[st[++tp] = u] = 1;
    for(int v : g[u]){
        if(!dfn[v]){
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(in[v]) low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u]){
        ++cnt;
        while(1){
            int x = st[tp];
            in[x] = 0, scc[x] = cnt, siz[cnt]++;
            --tp;
            if(x == u) break; 
        }
    }
}
void dfs(int u, int fa){
    if(in[u]) return;
    in[u] = 1;
    if(fa) e[fa].emplace_back(u);
    for(int v : tmp[u]) dfs(v, u);
}

int get(int u, int v){
    return (dfn[u] < dfn[v] ? u : v);
}
void init(int u, int fa){
    dis[u] = dis[fa] + siz[u];
    f[dfn[u] = ++tsp][0] = fa;
    for(int v : e[u]) init(v, u);
}
int Lca(int u, int v){
    if(u == v) return u;
    if(dfn[u] > dfn[v]) swap(u, v);
    u = dfn[u] + 1, v = dfn[v];
    int d = lg[v - u + 1];
    return get(f[u][d], f[v - (1 << d) + 1][d]); 
}

void build(){
    sort(a + 1, a + 1 + r, [](int x, int y){
            return dfn[x] < dfn[y];
    });
    r = unique(a + 1, a + 1 + r) - a - 1;
    _tot = tot = tp = 0;
    st[++tp] = cnt;
    sh[cnt] = _sh[cnt] = 0;
    auto len = [](int x, int y){ return dis[f[dfn[y]][0]] - dis[x];  };
    for(int i = 1; i <= r; ++i){
        if(a[i] == cnt) continue;
        int l = Lca(a[i], st[tp]);
        if(l != st[tp]){
            while(dfn[l] < dfn[st[tp - 1]]){
                adde(st[tp - 1], st[tp], len(st[tp - 1], st[tp]));
                --tp;
            }
            if(l != st[tp - 1]) sh[l] = _sh[l] = 0, adde(l, st[tp], len(l, st[tp])), st[tp] = l;
            else adde(st[tp - 1], st[tp], len(st[tp - 1], st[tp])), --tp;
        }
        sh[a[i]] = _sh[a[i]] = 0;
        st[++tp] = a[i];
    }
    for(int i = 1; i < tp; ++i) adde(st[i], st[i + 1], len(st[i], st[i + 1]));
}
void clear(int u){
    vis[u] = bk[u] = 0;
    for(int k = sh[u]; k; k = s[k].pre){
        clear(s[k].v);
    }
}
int ans = 0;
void get(int u){
    if(vis[u]) return;
    vis[u] = 1;
    for(int k = sh[u]; k; k = s[k].pre) vise[k] = 1, get(s[k].v);
}
void rev(int u){
    if(bk[u]) return;
    bk[u] = 1;
    if(vis[u]) ans += siz[u];
    for(int k = _sh[u]; k; k = _s[k].pre){
        if(vise[k]) ans += _s[k].w;
        rev(_s[k].v);
    } 
}
int main(){
    cin.tie(nullptr)->sync_with_stdio(0);
    cin >> n >> m >> q >> k;
    for(int i = 0; (1 << i) <= n; ++i) lg[(1 << i)] = i;
    for(int i = 1; i <= m; ++i){
        int u, v;
        cin >> u >> v;
        g[u].emplace_back(v);
    }

    for(int i = 1; i <= n; ++i){
        if(!lg[i]) lg[i] = lg[i - 1];
        if(!dfn[i]) tarjan(i);
    }
    in.reset();
    for(int i = 1; i <= n; ++i){
        for(int v : g[i]){
            if(scc[i] != scc[v]) tmp[scc[i]].emplace_back(scc[v]);
        }
    }
    for(int i = 1; i <= cnt; ++i) sort(tmp[i].begin(), tmp[i].end(), greater<int>());
    dfs(cnt, 0);
    memset(dfn, 0, sizeof(dfn));
    tsp = 0;
    init(cnt, 0);
    for(int j = 1; (1 << j) <= cnt; ++j){
        for(int i = 1; i + (1 << j) - 1 <= cnt; ++i){
            f[i][j] = get(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        }
    }

    while(q--){
        int s, t;
        cin >> s >> t;
        r = 0;
        a[++r] = scc[s], a[++r] = scc[t];
        for(int i = 1; i <= k; ++i){
            int u, v; cin >> u >> v;
            u = scc[u], v = scc[v];
            add[i] = {u, v};
            a[++r] = u, a[++r] = v;
        }
        build();
        clear(cnt);
        for(int i = 1; i <= k; ++i){
            if(add[i].u != add[i].v) adde(add[i].u, add[i].v, 0);
        }
        ans = 0;
        get(scc[s]);
        rev(scc[t]);
        cout << ans << '\n';
    }
    return 0;
} 
posted @ 2025-09-16 23:39  Hengsber  阅读(1)  评论(0)    收藏  举报