20241101

forgotmyhandle發表於2024-11-04

T1

美麗的序列

dp 中記錄每個數上一次出現位置和當前位置的差,和 \(7\)(或這個數)取 \(\min\)。狀態數很少,直接做即可。

程式碼
#include <iostream>
#include <unordered_map>
#include <vector>
#include <map>
using namespace std;
const int P = 1000000007;
inline void Madd(int& x, int y) { (x += y) >= P ? (x -= P) : 0; }
int n, m;
int a[10];
bool u[10];
unordered_map<int, int> mp;
int _mp[310005];
int cnt;
int out[310005][12];
int pw[12];
void dfs(int x, int cur) {
    if (x == m + 1) {
        if (!u[0]) 
            return;
        if (!mp.count(cur)) 
            _mp[mp[cur] = ++cnt] = cur;
        return;
    }
    for (int i = 0; i < 7; i++) {
        if (!u[i]) {
            u[i] = 1;
            dfs(x + 1, cur + i * pw[x - 1]);
            u[i] = 0;
        }
    }
    dfs(x + 1, cur + 7 * pw[x - 1]);
}
int dp[105][310005];
int main() {
    freopen("seq.in", "r", stdin);
    freopen("seq.out", "w", stdout);
    pw[0] = 1;
    for (int i = 1; i <= 9; i++) pw[i] = pw[i - 1] * 10;
    cin >> n >> m;
    for (int i = 1; i <= m; i++) cin >> a[i];
    dfs(1, 0);
    int ecnt = 0;
    for (int i = 1; i <= cnt; i++) {
        int tmp = _mp[i];
        for (int j = 0; j < m; j++) {
            if ((_mp[i] % pw[j + 1]) / pw[j] != 7) 
                tmp += pw[j];
        }
        for (int j = 1; j <= m; j++) {
            int t = (_mp[i] % pw[j]) / pw[j - 1];
            if (t >= a[j] - 1) {
                t = (tmp % pw[j]) / pw[j - 1];
                out[i][j] = mp[tmp - t * pw[j - 1]];
            }
        }
    }
    for (int i = 1; i <= m; i++) {
        int x = 0;
        for (int j = 1; j <= m; j++) 
            x = x + 7 * (i != j) * pw[j - 1];
        dp[1][mp[x]] = 1;
    }
    int ans = 0;
    for (int i = 2; i <= n; i++) {
        for (int j = 1; j <= cnt; j++) {
            if (!dp[i - 1][j]) 
                continue;
            for (int k = 1; k <= m; k++) 
                (out[j][k]) ? Madd(dp[i][out[j][k]], dp[i - 1][j]) : void();
        }
    }
    for (int i = 1; i <= cnt; i++) Madd(ans, dp[n][i]);
    cout << ans << "\n";
    return 0;
}

T2

開一棵線段樹維護原序列,每次操作相當於合併左右兒子或交換左右兒子。只考慮把右兒子併到左兒子的操作,則另一種操作可以透過再交換左右兒子實現。考慮對每一層打標記,注意到如果發生了合併操作,則這次合併之前的操作都是沒用的,所以只需要關心最後一次合併操作以及其之後的交換操作。由於每層的每個點操作的進度不一樣,考慮在每一層記一個時間軸。對每個點記錄其當前執行到了哪一個合併操作,然後 pushdown 一個點的時候就檢查它是否還有沒執行的合併操作,然後再看是否要交換左右兒子。合併都是線段樹合併,容易發現複雜度是對的。

程式碼
#include <iostream>
#define int long long
#define lowbit(x) ((x) & (-(x)))
using namespace std;
int n, q;
int a[600005];
struct node {
    int l, r, s;
    int tg;
} T[50000005];
int lm[30], t[30]; // last merge, cur time
struct Segment_Tree {
    int ncnt;
    int New(int d) { T[++ncnt] = (node) { 0, 0, 0, t[d] }; return ncnt; }
    void pushdown(int o, int d) {
        if (T[o].tg < lm[d]) 
            T[o].l = Merge(T[o].l, T[o].r, d - 1), T[o].r = 0, T[o].tg = lm[d];
        if ((t[d] ^ T[o].tg) & 1) 
            swap(T[o].l, T[o].r);
        T[o].tg = t[d];
    }
    int Merge(int x, int y, int d) {
        if (!x || !y) 
            return x | y;
        if (!d) {
            T[x].s += T[y].s;
            return x;
        }
        pushdown(x, d);
        pushdown(y, d);
        T[x].l = Merge(T[x].l, T[y].l, d - 1);
        T[x].r = Merge(T[x].r, T[y].r, d - 1);
        T[x].s = T[T[x].l].s + T[T[x].r].s;
        return x;
    }
    void Add(int& o, int l, int r, int x, int y, int d) {
        if (!o) 
            o = New(d);
        if (l == r) {
            T[o].s += y;
            return;
        }
        pushdown(o, d);
        int mid = (l + r) >> 1;
        x <= mid ? Add(T[o].l, l, mid, x, y, d - 1) : Add(T[o].r, mid + 1, r, x, y, d - 1);
        T[o].s = T[T[o].l].s + T[T[o].r].s;
    }
    int Query(int o, int l, int r, int L, int R, int d) {
        if (!o) 
            return 0;
        if (L <= l && r <= R) 
            return T[o].s;
        pushdown(o, d);
        int mid = (l + r) >> 1;
        if (R <= mid) 
            return Query(T[o].l, l, mid, L, R, d - 1);
        if (L > mid) 
            return Query(T[o].r, mid + 1, r, L, R, d - 1);
        return Query(T[o].l, l, mid, L, R, d - 1) + Query(T[o].r, mid + 1, r, L, R, d - 1);
    }
} seg;
int rt;
signed main() {
    freopen("jeden.in", "r", stdin);
    freopen("jeden.out", "w", stdout);
    cin >> n >> q;
    int d = -1;
    for (int x = n; x; x /= 2) ++d;
    // cout << __lg(n) << "\n";
    for (int i = 0; i < n; i++) cin >> a[i], seg.Add(rt, 0, n - 1, i, a[i], d);
    while (q--) {
        string s;
        int x, y;
        cin >> s >> x;
        if (s == "add") {
            cin >> y;
            seg.Add(rt, 0, n - 1, x, y, d);
        } else if (s == "sum") {
            cin >> y;
            cout << seg.Query(rt, 0, n - 1, x, y, d) << "\n";
        } else if (s == "and") {
            for (int i = 1; i <= d; i++) {
                if (!(x & (1 << (i - 1)))) 
                    lm[i] = ++t[i];
            }
        } else if (s == "or") {
            for (int i = 1; i <= d; i++) {
                if (x & (1 << (i - 1))) 
                    lm[i] = ++t[i], ++t[i];
            }
        } else 
            for (int i = 1; i <= d; i++) t[i] += (x >> (i - 1)) & 1;
    }
    return 0;
}

T3

朋友

首先每條魚的行動範圍是一段區間。然後會發現在欽定了最大的聚集位置之後,其兩邊都不會互相產生任何影響,也就是兩邊獨立。因此直接考慮區間 dp,轉移列舉當前區間中最大的聚集地,然後令這個區間中所有能走到這個聚集地的都走到這個聚集地,算一下貢獻即可。

程式碼
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
int n, m, w;
int h[10005];
int p[205], J[205];
int mx[17][10005];
int lg2[10005];
int L[10005], R[10005];
int d[405], dcnt;
inline int Qmax(int l, int r) {
    int k = lg2[r - l + 1];
    return max(mx[k][l], mx[k][r - (1 << k) + 1]);
}
int S[405][405][405];
int dp[405][405];
inline int C2(int x) { return x * (x - 1) / 2; }
signed main() {
    freopen("friend.in", "r", stdin);
    freopen("friend.out", "w", stdout);
    cin >> n >> m >> w;
    lg2[0] = -1;
    for (int i = 1; i <= n; i++) cin >> h[i], h[i] = w - h[i], mx[0][i] = h[i], lg2[i] = lg2[i - 1] + ((i & (i - 1)) == 0);
    for (int i = 1; (1 << i) <= n; i++) {
        for (int j = 1; j + (1 << i) - 1 <= n; j++) 
            mx[i][j] = max(mx[i - 1][j], mx[i - 1][j + (1 << (i - 1))]);
    }
    for (int i = 1; i <= m; i++) {
        cin >> p[i] >> J[i];
        int l = 1, r = p[i], mid;
        L[i] = p[i] + 1;
        R[i] = p[i] - 1;
        while (l <= r) {
            mid = (l + r) >> 1;
            if (Qmax(mid, p[i]) < J[i]) 
                L[i] = mid, r = mid - 1;
            else 
                l = mid + 1;
        }
        l = p[i], r = n;
        while (l <= r) {
            mid = (l + r) >> 1;
            if (Qmax(p[i], mid) < J[i]) 
                R[i] = mid, l = mid + 1;
            else 
                r = mid - 1;
        }
        L[i] = max(1ll, L[i] - 1);
        R[i] = min(n, R[i] + 1);
        d[++dcnt] = L[i];
        d[++dcnt] = R[i];
    }
    sort(d + 1, d + dcnt + 1);
    dcnt = unique(d + 1, d + dcnt + 1) - d - 1;
    for (int i = 1; i <= m; i++) {
        L[i] = lower_bound(d + 1, d + dcnt + 1, L[i]) - d;
        R[i] = lower_bound(d + 1, d + dcnt + 1, R[i]) - d;
    }
    for (int i = 1; i <= dcnt; i++) {
        for (int j = 1; j <= m; j++) {
            if (L[j] <= i && i <= R[j]) 
                ++S[i][L[j]][R[j]];
        }
        for (int j = dcnt; j; j--) {
            for (int k = 1; k <= dcnt; k++)     
                S[i][j][k] += S[i][j + 1][k] + S[i][j][k - 1] - S[i][j + 1][k - 1];
        }
    }
    for (int i = 1; i <= dcnt; i++) dp[i][i] = C2(S[i][i][i]);
    for (int i = 2; i <= dcnt; i++) {
        for (int l = 1; l + i - 1 <= dcnt; l++) {
            int r = l + i - 1;
            for (int k = l; k <= r; k++) dp[l][r] = max(dp[l][r], dp[l][k - 1] + dp[k + 1][r] + C2(S[k][l][r]));
        }
    }
    cout << dp[1][dcnt] << "\n";
    return 0;
}

T4

環覆蓋

咕咕咕。