2200左右的DS题单

字典树

Beautiful Subarrays

一眼转换为前缀和形式, 然后字典树计数即可

复制代码
#include <bits/stdc++.h> 
using namespace std;
#define endl "\n" 
typedef long long ll;

const int N = 1e6 + 100;
int Trie[N * 32][2], cnt, n, k;
int num[N * 32];
ll ans;

void insert(int x) {
    int now = 0;
    for (int i = 30; i >= 0; i--) {
        int j = x >> i & 1;
        if (!Trie[now][j]) Trie[now][j] = ++cnt;
        now = Trie[now][j];
        num[now]++;
    }
}

void query(int x) {
    int now = 0;
    for (int i = 30; i >= 0; i--) {
        int j = x >> i & 1;
        if (k >> i & 1) {
            if(!Trie[now][!j]) return;
            now = Trie[now][!j];
        } else {
            ans += num[Trie[now][!j]];
            if(!Trie[now][j]) return;
            now = Trie[now][j];
        }
    }
    ans += num[now];
}

void solve() {
    cin >> n >> k;
    ll now = 0;
    insert(0);
    for(int i = 1; i <= n; i++) {
        int x; cin >> x;
        now ^= x;
        insert(now);
        query(now);
    }
    cout << ans << endl;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();

    return 0;
}
View Code
复制代码

Choosing The Commander

还是 01 trie 树, 和上一题基本上一模一样

复制代码
// LUOGU_RID: 142877460
#include <bits/stdc++.h> 
using namespace std;
#define endl "\n" 
typedef long long ll;

const int N = 1e5 + 100;
int Trie[N * 32][2], cnt, n, p, l;
int num[N * 32];
ll ans;

void insert(int x) {
    int now = 0;
    for (int i = 31; i >= 0; i--) {
        int j = x >> i & 1;
        if (!Trie[now][j]) Trie[now][j] = ++cnt;
        now = Trie[now][j];
        num[now]++;
    }
}

void erase(int x) {
    int now = 0;
    for (int i = 31; i >= 0; i--) {
        int j = x >> i & 1;
        now = Trie[now][j];
        num[now]--;
    }
}

void query() {
    int now = 0;
    for (int i = 31; i >= 0; i--) {
        int j = p >> i & 1;
        if (l >> i & 1) {
            ans += num[Trie[now][j]];
            if(!num[Trie[now][!j]]) return;
            now = Trie[now][!j];
        } else {
            if(!num[Trie[now][j]]) return;
            now = Trie[now][j];
        }
    }
}

void solve() {
    int q; cin >> q;
    while (q--) {
        int op; cin >> op;
        if (op == 1) {
            int x; cin >> x;
            insert(x);
        } else if (op == 2)  {
            int x; cin >> x;
            erase(x);
        } else {
            cin >> p >> l;
            ans = 0;
            query();
            cout << ans << endl;
        }
    }
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();

    return 0;
}
View Code
复制代码

线段树/树状数组

Closest Equals

类似于 HH的项链 的离线做法

首先我们可以知道对于一个点计算距离真正有用的一定是前一个点(因为要求最短距离)

那么我们可以用线段树维护一个 iprei 最小值

为了保证在区间 [ l, r ] 之间我们离线下来每个询问, 从 1 到 n 的处理每个询问

每到一个点 i 我们修改 preiiprei 这样保证在查询时只会查询到区间在 [ l , r ] 区间的最小值

复制代码
#include <bits/stdc++.h> 
using namespace std;
#define endl "\n" 
typedef long long ll;

const int N = 5e5 + 100;

int a[N], pre[N], pos[N], Seg[N << 2], res[N];
vector<int> vec;
vector<array<int, 2>> g[N];

int get_idx(int x) {
    return lower_bound(vec.begin(), vec.end(), x) - vec.begin() + 1;
}

void pushup(int id) {
    Seg[id] = min(Seg[id << 1], Seg[id << 1 | 1]);
}

void modify(int id, int l, int r, int pos, int v) {
    if (l == r) {
        Seg[id] = v;
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid) modify(id << 1, l, mid, pos, v);
    else modify(id << 1 | 1, mid + 1, r, pos, v);
    pushup(id);
}

int query(int id, int l, int r, int x, int y) {
    if (x <= l && y >= r) return Seg[id];
    int mid = (l + r) >> 1, ans = INT_MAX;
    if (x <= mid) ans = min(ans, query(id << 1, l, mid, x, y));
    if(y > mid) ans = min(ans, query(id << 1 | 1, mid + 1, r, x, y));
    return ans;
}

void solve() {
    int n, m; cin >> n >> m;
    for (int i = 1; i <= 4 * N; i++) Seg[i] = INT_MAX;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        vec.push_back(a[i]);
    }

    for (int i = 1; i <= m; i++) {
        int l, r; cin >> l >> r;
        g[r].push_back({l, i});
    }
    sort(vec.begin(),vec.end());
    vec.erase(unique(vec.begin(), vec.end()), vec.end());

    for(int i = 1; i <= n; i++) {
        int id = get_idx(a[i]);
        pre[i] = pos[id];
        pos[id] = i;
        // cout << i << ' ' << pre[i] << endl; 
    }

    for(int r = 1; r <= n; r++) {
        if(pre[r]) modify(1, 1, n, pre[r], r - pre[r]);
        for(auto [l, id] : g[r]) {
            // cout << l << ' ' << r << endl;
            res[id] = query(1, 1, n, l, r);
        }
    } 

    for(int i = 1; i <= m; i++) {
        // cout << res[i] << endl;
        cout << (res[i] == INT_MAX ? -1 : res[i]) << endl;
    }
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();

    return 0;
}
View Code
复制代码

 Wine Factory (Easy Version)

用线段树维护一个类似于区间合并的 dp 

参考 : Codeforces Hello 2024 ABCDF1F2 讲解

把每个区间多出来的水, 多出来的体积, 答案都维护出来

转移时一定是从左边多出来的水转移到右边多出来的体积

复制代码
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long 
typedef long long ll;

const int N = 5e5 + 100;

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

struct node {
    int s, r, d;
} Seg[N << 2];

void pushup(int id) {
    int v = min(Seg[id << 1].s, Seg[id << 1 | 1].r);
    Seg[id].d = Seg[id << 1].d + Seg[id << 1 | 1].d + v;
    Seg[id].s = Seg[id << 1].s + Seg[id << 1 | 1].s - v;
    Seg[id].r = Seg[id << 1].r + Seg[id << 1 | 1].r - v;

}

void modify(int id, int l, int r, int pos, int v1, int v2) {
    if(l == r) {
        Seg[id].d = min(v1, v2);
        Seg[id].s = max(0ll, v1 - v2);
        Seg[id].r = max(0ll, v2 - v1);
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) modify(id << 1, l, mid, pos, v1, v2);
    else modify(id << 1 | 1, mid + 1, r, pos, v1, v2);
    pushup(id);
}

void solve() {
    int n, q; cin >> n >> q;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> b[i];
    for (int i = 1; i < n; i++) cin >> c[i];

    for (int i = 1; i <= n; i++) modify(1, 1, n, i, a[i], b[i]);

    while (q--) {
        int p, x, y, z; cin >> p >> x >> y >> z;
        modify(1, 1, n, p, x, y);
        cout << Seg[1].d << endl;
    }
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();
    return 0;
}
View Code
复制代码

 Pillars

权值线段树(平衡树)优化 dp

朴素dp思路就是

f[i]=max(f[j])+1

满足| bjbi>=d|

max 可以用权值线段树优化 复杂度变成 nlogn

复制代码
#include <bits/stdc++.h> 
using namespace std;
#define endl "\n" 
#define int long long
typedef long long ll;

const int N = 1e5 + 100;

int a[N], f[N], pre[N];
vector<ll> vec;
pair<int, int> Seg[N << 4];

int get_idx(int x) {
    return lower_bound(vec.begin(), vec.end(), x) - vec.begin() + 1;
}

pair<int, int> operator + (const pair<int, int> &l, const pair<int, int> &r) {
    pair<int, int> ans;
    auto [v1, pos1] = l;
    auto [v2, pos2] = r;
    if (v1 > v2) ans = l;
    else ans = r;
    return ans;
}

void pushup(int id) {
    Seg[id] = Seg[id << 1] + Seg[id << 1 | 1];
}

void modify(int id, int l, int r, int pos, int v) {
    if (l == r) {
        Seg[id] = {v, pos};
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid) modify(id << 1, l, mid, pos, v);
    else modify(id << 1 | 1, mid + 1, r, pos, v);
    pushup(id);
}

pair<int, int> query(int id, int l, int r, int x, int y) {
    if (x <= l && y >= r) return Seg[id];
    int mid = (l + r) >> 1;
    pair<int, int> tl, tr;
    tl = tr = {INT_MIN, 0};
    if(x <= mid) tl = query(id << 1, l, mid, x, y);
    if(y > mid) tr = query(id << 1 | 1, mid + 1, r, x, y);
    return tl + tr;
}

void solve() {
    int n, d; cin >> n >> d;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        vec.push_back(a[i]);
        vec.push_back(a[i] + d);
        vec.push_back(a[i] - d);
    }
    vec.push_back(0); vec.push_back(1e15 + 100);
    sort(vec.begin(),vec.end());
    vec.erase(unique(vec.begin(), vec.end()), vec.end());   

    int sz = vec.size(), maxn = 0, mpos = -1;
    map<ll, int> vis;

    for (int i = 1; i <= n; i++) {
        int idl = get_idx(a[i] - d);
        int idr = get_idx(a[i] + d);
        int id = get_idx(a[i]);
        pair<int, int> l = query(1, 1, sz, 1, idl);
        pair<int, int> r = query(1, 1, sz, idr, sz);
        pair<int, int> ans = l + r;
        auto [v, pos] = ans;
        f[i] = v + 1;
        pre[i] = vis[pos];
        vis[id] = i; 
        if(f[i] > maxn) maxn = f[i], mpos = i;
        modify(1, 1, sz, id, f[i]);
    }

    // for(int i = 1; i <= n; i++) cout << i << ' ' << pre[i] << endl;
    vector<int> ans;
    cout << maxn << endl;
    ans.push_back(mpos);
    while (pre[mpos]) {
        mpos = pre[mpos];
        ans.push_back(mpos);
    }
    reverse(ans.begin(), ans.end());
    for(auto i : ans) cout << i << ' ';
    cout << endl;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();

    return 0;
}
View Code
复制代码

 Big Maximum Sum

纯纯大水题, 就类似于一个维护最多 1 的个数线段树的 pushup 操作维护出 maxn , l_maxn ,  r_maxn 从左到右合并就行

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
typedef long long ll; 

const int N = 300000;

int ord[N], mx[N], lmx[N], rmx[N], sum[N];
int Ansmx[N], Anslmx[N], Ansrmx[N], Anssum[N];

void pushup(int id) {
    int l = id, r = ord[id + 1];
    
    Ansmx[id + 1] = max({Ansmx[id], mx[r], Ansrmx[id] + lmx[r]});
    Anslmx[id + 1] = max(Anslmx[id], Anssum[id] + lmx[r]);
    Ansrmx[id + 1] = max(rmx[r], sum[r] + Ansrmx[id]);
    Anssum[id + 1] = Anssum[id] + sum[r];
}

void solve() {
    int n, m; cin >> n >> m;

    vector<vector<int>> a(n + 1);

    memset(mx, 128, sizeof(mx));
    memset(lmx, 128, sizeof(lmx));
    memset(rmx, 128, sizeof(rmx));

    for (int i = 1; i <= n; i++) {
        int sz; cin >> sz;
        a[i].resize(sz + 1);
        for (int j = 1; j <= sz; j++) cin >> a[i][j];
        
        vector<int> f(sz + 1);
        for (int j = 1; j <= sz; j++) {
            sum[i] += a[i][j];
            f[j] = max(a[i][j], f[j - 1] + a[i][j]);
            mx[i] = max(mx[i], f[j]);
            lmx[i] = max(lmx[i], sum[i]);
        }

        int now = 0;
        for (int j = sz; j >= 1; j--) {
            now += a[i][j];
            rmx[i] = max(rmx[i], now);
        }
    }

    for (int i = 1; i <= m; i++) cin >> ord[i];

    Ansmx[1] = mx[ord[1]]; 
    Anslmx[1] = lmx[ord[1]];
    Ansrmx[1] = rmx[ord[1]];
    Anssum[1] = sum[ord[1]];

    for (int i = 1; i < m; i++) pushup(i);

    cout << Ansmx[m] << endl;

}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while(T--) solve();

    solve();

    return 0;
}
View Code
复制代码

 Domino Principle

第一次自己独立写出来 2200 的题,但是写完以后看了下题解, 发现好像写的有点复杂了

做法 : 首先这种题肯定是需要从位置的后面往前面维护的, 那么我们只需要维护一下每个点能到的最右边的点就行

假定对于一块牌倒下去可以覆盖的区间是 [ l , r ] 那么我们只需要知道这段区间中所有的牌能覆盖到的最右边端点是哪(查询区间 max 的线段树), 假定为 nowr 那么区间 [ l, nowr ] 就是统计答案的区间 (可以用树状数组计数)

所以一颗线段树 + 树状数组 + 离散化 即可通过本题

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n";
typedef long long ll;

const int N = 3e5 + 10;

template <typename T>
struct BIT {
    T tr[N];
    int n;
    void init(int n_){
        n = n_;
        for(int i = 0; i <= n; i ++) tr[i] = 0;
    }
    inline int lowbit(int x) {return x & -x;}
    void add(int x, T v){
        for(int i = x; i <= n; i += lowbit(i)) tr[i] += v;
    }
    T sum(int x){
        T res = 0;
        for(int i = x; i > 0; i -= lowbit(i)) res += tr[i];
        return res;
    }
    // 查询最大位置pos,使得1 ~ pos 和小于等于s,a1 + a2 + ....apos <= s 
    int query(T s){
        int pos = 0;
        for(int i = 20; i >= 0; i --){
            if(pos + (1 << i) <= n && tr[pos + (1 << i)] <= s){
                pos += (1 << i);
                s -= tr[pos];
            }
        }
        return pos;
    }
 
    T sum(int l, int r){ return sum(r) - sum(l - 1);}
    void add(int l, int r, T v) {add(l, v); add(r + 1, -v);};
};

struct NODE {
    int x, h, id;
} a[N];

bool cmp(NODE na, NODE nb) {
    return na.x < nb.x;
}

vector<int> vec;
int ans[N];
BIT<int> T;

int get_idx(int x) {
    return lower_bound(vec.begin(), vec.end(), x) - vec.begin() + 1;
}

int Seg[N << 2];

void pushup(int id) {
    Seg[id] = max(Seg[id << 1], Seg[id << 1 | 1]);
}

void modify(int id, int l, int r, int pos, int v) {
    if (l == r) {
        Seg[id] = v;
        return;
    }
    int mid = l + r >> 1;
    if (pos <= mid) modify(id << 1, l, mid, pos, v);
    else modify(id << 1 | 1, mid + 1, r, pos, v);
    pushup(id);
}

int query(int id, int l, int r, int x, int y) {
    if (x <= l && y >= r) return Seg[id];
    int mid = l + r >> 1, res = INT_MIN;
    if (x <= mid) res = max(res, query(id << 1, l, mid, x, y));
    if (y > mid) res = max(res, query(id << 1 | 1, mid + 1, r, x, y));
    return res;
}

void solve() {
    int n; cin >> n;
    for (int i = 1; i <= n; i++) {
        int x, h; cin >> x >> h;
        a[i] = {x, h, i};
        vec.push_back(x);
        vec.push_back(x + h - 1);
        vec.push_back(x + 1);
    }
    
    sort(a + 1, a + 1 + n, cmp);
    sort(vec.begin(),vec.end());
    vec.erase(unique(vec.begin(), vec.end()), vec.end());

    int sz = vec.size();
    T.init(sz + 10);

    for (int i = n; i >= 1; i--) {
        auto [x, h, id] = a[i];
        int pos = get_idx(x), posl = get_idx(x + 1), posr = get_idx(x + h - 1);
        T.add(pos, 1);
        int r = query(1, 1, sz, posl, posr);
        ans[id] = T.sum(pos, max(r, posr));
        modify(1, 1, sz, pos, max(posr, r));
        // cout << x << ' ' << vec[max(r, posr) - 1] << endl;
    }

    for (int i = 1; i <= n; i++) cout << ans[i] << ' '; 
}

int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T; cin >> T;
    // while (T--) solve();
    solve();

    return 0;
}
View Code
复制代码

Irrigation

首先显而易见,对于某个值 x 当到某个临界点时会全是一种值然后就是彼此之间循环加(变成一个查询 k 大的问题)

对于临界点的处理,我们把到达某个点全变成该点边权代价处理出来, 记代价为 val ,那么我们临界点的值 v 一定是 val [ i + 1] >= v > val [ i ]

我们离线下来询问, 从小到大加点, 再用权值线段树二分查询 k 大就行

复制代码
#include <bits/stdc++.h> 
using namespace std;
#define endl "\n"
#define int long long
typedef long long ll;

const int N = 5e5 + 100;

int cnt[N], Q[N], Seg[N * 4];

void pushup(int id) {
    Seg[id] = Seg[id * 2] + Seg[id * 2 + 1];
}

void modify(int id, int l, int r, int pos, int v) {
    if (l == r) {
        Seg[id] = v;
        return;
    }
    int mid = l + r >> 1;
    if (pos <= mid) modify(id * 2, l, mid, pos, v);
    else modify(id * 2 + 1, mid + 1, r, pos, v);
    pushup(id);
}

int query(int id, int l, int r, int k) {
    if (l == r) return l;
    int mid = l + r >> 1;
    if (Seg[id * 2] >= k) return query(id * 2, l, mid, k);
    else return query(id * 2 + 1, mid + 1, r, k - Seg[id * 2]);
}

void solve() {
    int n, m, q; cin >> n >> m >> q;
    vector<array<int, 2>> a;
    map<ll, int> que, ans;
    for (int i = 1; i <= n; i++) {
        int x; cin >> x;
        cnt[x]++;
    }

    a.push_back({0, 0});
    for (int i = 1; i <= m; i++) a.push_back({cnt[i], i});

    sort(a.begin() + 1, a.end());
    
    vector<ll> pre(m + 1, 0), val(m + 1, 0);
    for (int i = 1; i <= m; i++) {
        pre[i] = pre[i - 1] + a[i][0];
        val[i] = a[i][0] * i - pre[i];
    }
    val.push_back(LLONG_MAX);

    for (int i = 1; i <= q; i++) {
        cin >> Q[i];
        que[Q[i]] = 1;
    }

    int p = 0;
    for (auto [x, y] : que) {
        int id = lower_bound(val.begin() + 1, val.end(), x - n) - val.begin() - 1;
        if (id <= 0) id = 0;
        for (int i = p + 1; i <= id; i++) modify(1, 1, m, a[i][1], 1);
        p = id;
        ans[x] = query(1, 1, m, ((x - val[id] - n) % id) ? (x - val[id] - n) % id : id);
    }
    for (int i = 1; i <= q; i++) {
        cout << ans[Q[i]] << endl;
    }
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    // int T; cin >> T;
    // while (T--) solve();
    solve();

    return 0;   
}
View Code
复制代码

 Maximize The Value

扫描线好题,codeforce 一些板刷 - zhujio 3.12

 

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
typedef long long ll;

const int N = 2e5 + 100;

vector<array<int, 2>> op[N];
vector<array<int, 3>> que[N];

struct node {
    int sum, pre, suf, val;
} Seg[N * 4];

node operator + (const node &l, const node &r) {
    node ans; ans.sum = l.sum + r.sum;
    ans.pre = max(l.pre, l.sum + r.pre);
    ans.suf = max(r.suf, r.sum + l.suf);
    ans.val = max({l.val, r.val, l.suf + r.pre});
    return ans;
}

void pushup(int id) {
    Seg[id] = Seg[id * 2] + Seg[id * 2 + 1];
}

void modify(int id, int l, int r, int pos, int v) {
    if (l == r) {
        Seg[id].sum += v;
        Seg[id].pre += v;
        Seg[id].suf += v;
        Seg[id].val += v;
        return;
    }
    int mid = l + r >> 1;
    if (pos <= mid) modify(id * 2, l, mid, pos, v);
    else modify(id * 2 + 1, mid + 1, r, pos, v);
    pushup(id);
}

node query(int id, int l, int r, int x, int y) {
    if (x <= l && y >= r) return Seg[id];
    int mid = l + r >> 1;
    if (x > mid) return query(id * 2 + 1, mid + 1, r, x, y);
    else if (y <= mid) return query(id * 2, l, mid, x, y);
    else return query(id * 2, l, mid, x, mid) + query(id * 2 + 1, mid + 1, r, mid + 1, y);
}

void solve() {
    int n, m; cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int l, r, v; cin >> l >> r >> v;
        op[l].push_back({v, i});
        op[r + 1].push_back({-v, i});
    }

    int q; cin >> q;
    for (int i = 1; i <= q; i++) {
        int pos, l, r; cin >> pos >> l >> r;
        que[pos].push_back({l, r, i});
    }

    vector<int> ans(q + 1);
    for (int i = 1; i <= n; i++) {
        for (auto [v, id] : op[i]) modify(1, 1, m, id, v);
        for (auto [l, r, id] : que[i]) ans[id] = query(1, 1, m, l, r).val;
    }

    for (int i = 1; i <= q; i++) cout << ans[i] << endl;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    solve();

    return 0;
}
View Code
复制代码

 A Growing Tree 

codeforce 一些板刷 3.12

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
typedef long long ll;

const int N = 1e6 + 100;

array<int, 3> op[N];
vector<int> g[N];

template <typename T>
struct BIT {
    T tr[N];
    int n;
    void init(int n_) {
        n = n_;
        for(int i = 0; i <= n; i++) tr[i] = 0;
    }
    int lowbit(int x) {return x & -x;}
    void add(int x, T v){
        for(int i = x; i <= n; i += lowbit(i)) tr[i] += v;
    }
    T sum(int x) {
        T res = 0;
        for(int i = x; i > 0; i -= lowbit(i)) res += tr[i];
        return res;
    }
    // 查询最大位置pos,使得1 ~ pos 和小于等于s,a1 + a2 + .... + apos <= s 
    int query(T s){
        int pos = 0;
        for(int i = 20; i >= 0; i--){
            if(pos + (1 << i) <= n && tr[pos + (1 << i)] <= s){
                pos += (1 << i);
                s -= tr[pos];
            }
        }
        return pos;
    }
 
    T sum(int l, int r) {return sum(r) - sum(l - 1);}
    void add(int l, int r, T v) {add(l, v); add(r + 1, -v);}
};

int L[N], R[N], dfn;
void dfs(int x, int fa) {
    L[x] = ++dfn;
    for (auto y : g[x]) {
        if (y == fa) continue;
        dfs(y, x);
    }
    R[x] = dfn;
}

BIT<int> T;

void solve() {
    int q; cin >> q;
    int sz = 1;
    dfn = 0;
    for (int i = 1; i <= q; i++) {
        int opt; cin >> opt;
        if (opt == 1) {
            int x; cin >> x;
            op[i] = {opt, sz + 1, 0};
            g[x].push_back(sz + 1);
            sz++;
        } else {
            int x, v; cin >> x >> v;
            op[i] = {opt, x, v};
        }
    }
    dfs(1, 1);
    vector<int> ans(sz + 1);
    T.init(sz + 100);
    for (int i = q; i >= 1; i--) {
        auto [opt, x, y] = op[i];
        if (opt == 1) {
            ans[x] = T.sum(L[x]);
        } else {    
            T.add(L[x], R[x], y);
        }
    }
    ans[1] = T.sum(1);
    for (int i = 1; i <= sz; i++) cout << ans[i] << " \n"[i == sz];
    for (int i = 0; i <= sz; i++) g[i].clear(), L[i] = R[i] = 0;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while (T--) solve();
    return 0;
}
View Code
复制代码

 

 

 哈希

Check Transcription

首先观察到只会有两种串, 那么我们去枚举前缀第一个串的长度, 那么对于另外一个串长度是可以算出来的(写的时候犯病了,一直在想另外一个串怎么处理)

那么这时我们其实两个串都知道了,只需要去遍历一遍 01 串 check 就行, 这个复杂度看起来是非常 O( | s | * | t | ) 的

但是我们枚举玩第一串长度后, 剩下长度必须与另外一个串出现次数是整除关系, 似乎可以剪枝掉很多? 复杂度有点不太懂怎么证明...

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n";
#define int long long
typedef long long ll;

//字符串hash , 使用时应在字符串首补一个符号
struct Hash
{
    vector<int> hs1, hs2, pn1, pn2;
    int length, base = 131, p1 = 1222827239, p2 = 1610612741;
public:
    explicit Hash(const string& s) //传入字符串初始下标应为1
    {
        length = (int)s.length() - 1;
        hs1.resize(length + 1);
        hs2.resize(length + 1);
        pn1.resize(length + 1);
        pn2.resize(length + 1);
        pn1[0] = pn2[0] = 1;
        for (int i = 1; i <= length; i++) {
            hs1[i] = (hs1[i - 1] * 1ll * base + s[i]) % p1;
            hs2[i] = (hs2[i - 1] * 1ll * base + s[i]) % p2;
            pn1[i] = pn1[i - 1] * 1ll * base % p1;
            pn2[i] = pn2[i - 1] * 1ll * base % p2;
        }
    }
    int gethash1(int l, int r) {
        if(l > r) return 0;
        return (hs1[r] - hs1[l - 1] * 1ll * pn1[r - l + 1] % p1 + p1) % p1;
    }
    int gethash2(int l, int r) {
        if(l > r) return 0;
        return (hs2[r] - hs2[l - 1] * 1ll * pn2[r - l + 1] % p2 + p2) % p2;  
    }
};

void solve() {
    string s1, s2; cin >> s1 >> s2;
    int n1 = s1.size(), n2 = s2.size();
    s1 = " " + s1;
    s2 = " " + s2; 
    Hash hash(s2);
    
    int cnt0 = 0, cnt1 = 0;
    for (int i = 1; i <= n1; i++) {
        cnt0 += s1[i] == '0';
        cnt1 += s1[i] == '1';
    }

    int ans = 0;
    for (int len = 1; len < n2; len++) {
        if (len * (s1[1] == '1' ? cnt1 : cnt0) >= n2) continue;
        int del = n2 - len * (s1[1] == '1' ? cnt1 : cnt0);
        if (del % (s1[1] == '1' ? cnt0 : cnt1)) continue;
        int tot =  del / (s1[1] == '1' ? cnt0 : cnt1);
        int val0 = -1, val1 = -1, v0 = -1, v1 = -1, l = 1, r;
        bool ok = true;
        for (int i = 1; i <= n1; i++) {
            if (s1[i] == s1[1]) {
                r = l + len - 1;
                if (val0 == -1) val0 = hash.gethash1(l, r), v0 = hash.gethash2(l, r);
                else {
                    if (val0 != hash.gethash1(l, r) || v0 != hash.gethash2(l, r)) {
                        ok = false;
                        break;
                    }
                }
                l = r + 1;
            } else {
                r = l + tot - 1;
                if (val1 == -1) val1 = hash.gethash1(l, r), v1 = hash.gethash2(l, r);
                else {
                    if (val1 != hash.gethash1(l, r) || v1 != hash.gethash2(l, r)) {
                        ok = false;
                        break;
                    }
                }
                l = r + 1;
            }
        }
        if (ok && (val0 != val1 || v0 != v1)) ans++;
    }
    cout << ans << endl;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T; cin >> T;
    // while (T--) solve();
    solve();

    return 0;
}
View Code
复制代码

 根号分治

Remainder Problem

根号分治 - zhujio 哈希冲突那题基本上一模一样

View Code

 

posted @   zhujio  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示