HDU 多校 2025 R3

T1

求和

考虑如果能对于每个 \(b_i\) 求出转移矩阵的 \(b_i\) 次方,那只需要把所有这些东西各加一个单位阵再全乘起来即可。但是这样显然会 TLE。先考虑把最后一步换成拿向量把所有东西乘一遍,然后考虑前面的部分。如果使用矩阵快速幂的话,就又发现既然都要把最终乘积和向量乘了,还不如直接把二进制拆分出来的东西都直接跟向量乘,最后再把和单位阵的乘积加回去。这样总复杂度就是 \(nk^2\log\) 了。

T5

平衡阵盘

还不会。

T6

巨龙守卫

考虑对着 \(l, r\) 数位 dp。 直接 \(dp_{i, p, a, b, c, 0 / 1, 0 / 1}\) 表示从低位往高位考虑到了第 \(i\) 位,下一位往这一位进位了 \(p\),填的数有 \(a\) 个数顺从上界而超越下界,\(b\) 个越下不越上,\(c\) 个都不越过,填的所有数的异或和是否服从上界,以及所有数的和(不算往这一位的进位)是否服从上界。然后转移直接枚举四种顺从状态里各放了多少 \(1\),暴力搞即可。

代码
#include <iostream>
#include <algorithm>
#include <string.h>
#include <time.h>
#define int long long
using namespace std;
const int P = 1000000007;
inline void Madd(int &x, int y) { (x += y) >= P ? (x -= P) : 0; }
int n, l, r, V1, V2;
int dp[33][21][11][11][11][2][2];
int C[20][20];
// cur digit, carry, obey up not down, obey not up but down, obey both, obey xor, obey sum
signed main() {
    int ttt = clock();
    // freopen("in.in", "r", stdin);
    // freopen("in.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    for (int i = C[0][0] = 1; i < 20; i++) {
        for (int j = C[i][0] = 1; j <= i; j++) 
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
    }
    int tc;
    cin >> tc;
    while (tc--) {
        cin >> n >> l >> r >> V1 >> V2;
        for (int i = 0; i < 32; i++) {
            for (int j = 0; j <= n * 2; j++) {
                for (int a = 0; a <= n; a++) {
                    for (int b = 0; a + b <= n; b++) {
                        for (int c = 0; a + b + c <= n; c++) {
                            for (int lx : { 0, 1 }) {
                                for (int ls : { 0, 1 }) 
                                    dp[i][j][a][b][c][lx][ls] = 0;
                            }
                        }
                    }
                }
            }
        }
        dp[0][0][0][0][n][1][1] = 1;
        for (int i = 0; i < 31; i++) {
            int _l = ((l >> i) & 1);
            int _r = ((r >> i) & 1);
            int _s = ((V1 >> i) & 1);
            int _x = ((V2 >> i) & 1);
            for (int j = 0; j <= n * 2; j++) {
                for (int a = 0; a <= n; a++) {
                    for (int b = 0; a + b <= n; b++) {
                        for (int c = 0; a + b + c <= n; c++) {
                            int d = n - a - b - c;
                            for (int lx : { 0, 1 }) {
                                for (int ls : { 0, 1 }) {
                                    if (!dp[i][j][a][b][c][lx][ls]) 
                                        continue;
                                    int cur = dp[i][j][a][b][c][lx][ls];
                                    for (int _a = 0; _a <= a; _a++) {
                                        for (int _b = 0; _b <= b; _b++) {
                                            for (int _c = 0; _c <= c; _c++) {
                                                for (int _d = 0; _d <= d; _d++) {
                                                    int xs = ((_a + _b + _c + _d) & 1), car = (_a + _b + _c + _d + j) / 2, s = ((_a + _b + _c + _d + j) & 1);
                                                    int X[2][2][2] = { { { d - _d, b - _b }, { a - _a, c - _c } }, { { _d, _b }, { _a, _c } } };
                                                    int Y[2][2] = { { 0, 0 }, { 0, 0 } };
                                                    int coe = C[a][_a] * C[b][_b] % P * C[c][_c] % P * C[d][_d] % P;
                                                    Y[_r][0] += X[0][0][0];
                                                    Y[_r][!_l] += X[0][0][1];
                                                    Y[1][0] += X[0][1][0];
                                                    Y[1][!_l] += X[0][1][1];
                                                    Y[0][!_l] += X[1][0][0];
                                                    Y[0][1] += X[1][0][1];
                                                    Y[_r][!_l] += X[1][1][0];
                                                    Y[_r][1] += X[1][1][1];
                                                    Madd(dp[i + 1][car][Y[1][0]][Y[0][1]][Y[1][1]][(xs < _x) ? 1 : (xs > _x ? 0 : lx)][(s < _s) ? 1 : (s > _s ? 0 : ls)], coe * cur % P);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        cout << dp[31][0][0][0][n][1][1] << "\n";
    }
    return 0;
}

T7

性质不同的数字

对端点离散化,异或哈希即可。

T8

01 环

注意到已经一样的位置不会动。然后考虑一个不一样的位置,如果下一个位置是一样的,那只好把这个位置 flip 掉。否则交换这两个位置更优,就交换一下。容易发现交换之后这两个位置就都合法了。然后就直接做完了。

T9

线段染色

考虑对染了色的位置 dp。转移考虑上一个染色的位置,合法要求这段区间中不能完整包含一条线段。于是转移区间为右端点为当前点的一段区间,线段树优化转移即可。注意 \(0\) 的问题。

代码
#include <iostream>
#include <vector>
#define int long long
using namespace std;
const int P = 1000000007;
int qpow(int x, int y = P - 2) {
    int ret = 1;
    while (y) {
        if (y & 1) 
            ret = ret * x % P;
        y >>= 1;
        x = x * x % P;
    }
    return ret;
}
struct Segment_Tree {
    int s[1000005];
    void Build(int o, int l, int r) {
        s[o] = 0;
        if (l == r) 
            return;
        int mid = (l + r) >> 1;
        Build(o << 1, l, mid);
        Build(o << 1 | 1, mid + 1, r);
    }
    void Add(int o, int l, int r, int x, int y) {
        if (l == r) 
            return s[o] = y, void();
        int mid = (l + r) >> 1;
        if (x <= mid) 
            Add(o << 1, l, mid, x, y);
        else 
            Add(o << 1 | 1, mid + 1, r, x, y);
        s[o] = (s[o << 1] + s[o << 1 | 1]) % P;
    }
    int Query(int o, int l, int r, int L, int R) {
        if (L <= l && r <= R) 
            return s[o];
        int mid = (l + r) >> 1;
        if (R <= mid) 
            return Query(o << 1, l, mid, L, R);
        if (L > mid) 
            return Query(o << 1 | 1, mid + 1, r, L, R);
        return (Query(o << 1, l, mid, L, R) + Query(o << 1 | 1, mid + 1, r, L, R)) % P;
    }
} seg;
int n, m;
int a[2500005], p[2500005];
int dp[2500005], f[2500005];
vector<int> vec[2500005];
signed main() {
    int i10 = qpow(10);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int tc;
    cin >> tc;
    while (tc--) {
        cin >> n >> m;
        p[0] = 1;
        for (int i = 1; i <= n; i++) {
            cin >> a[i], vec[i].clear();
            a[i] = a[i] * i10 % P;
            if (a[i] == 1) 
                p[i] = p[i - 1];
            else 
                p[i] = p[i - 1] * (P + 1 - a[i]) % P;
        }
        a[n + 1] = 1;
        p[n + 1] = p[n];
        for (int i = 1, x, y; i <= m; i++) {
            cin >> x >> y;
            vec[y].emplace_back(x);
        }
        seg.Build(1, 0, n);
        seg.Add(1, 0, n, 0, 1);
        for (int i = 1, l = 0; i <= n + 1; i++) {
            dp[i] = p[i - 1] * a[i] % P * seg.Query(1, 0, n, l, i - 1) % P;
            for (auto v : vec[i]) l = max(l, v);
            if (a[i] == 1) 
                l = i;
            seg.Add(1, 0, n, i, dp[i] * qpow(p[i]) % P);
        }
        cout << dp[n + 1] << "\n";
    }
    return 0;
}

T10

坚船利炮

连通块点数平方和即为连通点对个数。 于是只需要求出两两距离为 \(i\) 的点对个数。点分治,按 dep 合并子树时 NTT 即可。求出这个之后答案是容易算的。

T11

难度调整

考虑 \(f_{i, j}\) 表示前 \(i\) 个数,最后一个改成了 \(j\) 的最小合法代价。有转移 \(f_{i, j} = \min\{ f_{i - 1, j - 1}, f_{i - 1, j} \} + |a_i - j|\)。发现是凸的,考虑 slope trick。维护差分数组,发现每次的变化相当于把第一个非负数前面加入一个 \(0\),然后把一段前缀 \(-1\),一段后缀 \(+1\)。直接平衡树维护即可。

似乎可以优先队列维护,但我还不会。

代码(平衡树)
#include <iostream>
#include <random>
#define int long long
using namespace std;
#define getchar() p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++
char buf[1<<21], *p1, *p2, ch;
long long read() {
    long long ret = 0, neg = 0; char c = getchar(); neg = (c == '-');
    while (c < '0' || c > '9') c = getchar(), neg |= (c == '-');
    while (c >= '0' && c <= '9') ret = ret * 10 + c - '0', c = getchar();
    return ret * (neg ? -1 : 1);
}
const int inf = 0x3f3f3f3f3f3f3f3f;
random_device rd;
mt19937_64 mtrand(rd());
struct node {
    int l, r, sz, v, tg;
    unsigned int p;
} T[1000005];
int ncnt;
inline int New(int v) {
    T[++ncnt] = (node) { 0, 0, 1, v, 0, mtrand() };
    return ncnt;
}
inline void tag(int x, int t) { T[x].v += t, T[x].tg += t; }
void pushdown(int x) {
    if (!T[x].tg) 
        return;
    if (T[x].l) 
        tag(T[x].l, T[x].tg);
    if (T[x].r) 
        tag(T[x].r, T[x].tg);
    T[x].tg = 0;
}
inline void pushup(int x) { T[x].sz = T[T[x].l].sz + T[T[x].r].sz + 1; }
void SplitV(int x, int &l, int &r, int v) {
    if (!x) 
        return l = r = 0, void();
    pushdown(x);
    if (T[x].v <= v) 
        SplitV(T[x].r, T[l = x].r, r, v);
    else 
        SplitV(T[x].l, l, T[r = x].l, v);
    pushup(x);
}
void SplitR(int x, int &l, int &r, int K) {
    if (!x) 
        return l = r = 0, void();
    pushdown(x);
    if (T[T[x].l].sz + 1 <= K) 
        SplitR(T[x].r, T[l = x].r, r, K - T[T[x].l].sz - 1);
    else 
        SplitR(T[x].l, l, T[r = x].l, K);
    pushup(x);
}
int Merge(int p, int q) {
    if (!p || !q) 
        return p | q;
    pushdown(p), pushdown(q);
    if (T[p].v > T[q].v) 
        swap(p, q);
    if (T[p].p < T[q].p) {
        T[p].r = Merge(T[p].r, q);
        pushup(p);
        return p;
    } else {
        T[q].l = Merge(p, T[q].l);
        pushup(q);
        return q;
    }
    return -1;
}
int n, ans;
int a[1000005];
int rt;
void dfs(int x) {
    pushdown(x);
    if (T[x].v < 0) 
        ans += T[x].v;
    if (T[x].l) 
        dfs(T[x].l);
    if (T[x].r) 
        dfs(T[x].r);
}
signed main() {
    int tc = read();
    while (tc--) {
        ncnt = ans = 0;
        n = read();
        for (int i = 1; i <= n; i++) a[i] = read(), ans += max(0ll, a[i] - n), a[i] = min(a[i], n), ans += a[i];
        rt = New(inf);
        for (int i = 1; i <= n; i++) {
            int a, b, c;
            SplitV(rt, a, b, 0);
            rt = Merge(a, Merge(New(0), b));
            SplitR(rt, a, b, ::a[i]);
            tag(a, -1), tag(b, 1);
            rt = Merge(a, b);
        }
        dfs(rt);
        cout << ans << "\n";
    }
    return 0;
}

12

核心共振

切比雪夫转曼哈顿,按横坐标排序之后分讨,BIT 随便维护。

代码
#include <iostream>
#include <algorithm>
#define lowbit(x) ((x) & (-(x)))
#define int __int128
using namespace std;
const int P = 1000000007;
long long n;
struct node {
    long long x, y, a;
} a[200005];
int d[200005], dcnt;
struct BIT {
    int bit[200005];
    inline void clear(int x) { for (; x; --x) bit[x] = 0; }
    void add(int x, int y) { for (; x <= n; x += lowbit(x)) bit[x] += y; }
    int query(int x) {
        int ret = 0;
        for (; x; x -= lowbit(x)) ret += bit[x];
        return ret;
    }
} bit[4];
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    long long tc;
    cin >> tc;
    while (tc--) {
        dcnt = 0;
        cin >> n;
        for (long long i = 1, x, y; i <= n; i++) {
            cin >> x >> y >> a[i].a;
            a[i].x = x + y, a[i].y = x - y;
            d[++dcnt] = a[i].y;
        }
        sort(d + 1, d + dcnt + 1);
        dcnt = unique(d + 1, d + dcnt + 1) - d - 1;
        sort(a + 1, a + n + 1, [](node a, node b) { return (a.x == b.x) ? (a.y < b.y) : (a.x < b.x); });
        int ans = 0; // ans /= 2
        for (int i = 1; i <= n; i++) {
            int t = lower_bound(d + 1, d + dcnt + 1, a[i].y) - d;
            int xx = a[i].x, yy = a[i].y;
            ans += a[i].a * (xx + yy) * bit[0].query(t) - bit[1].query(t) + (xx + yy) * bit[2].query(t) - a[i].a * bit[3].query(t);
            bit[0].add(t, 1), bit[1].add(t, a[i].a * (xx + yy)), bit[2].add(t, a[i].a), bit[3].add(t, xx + yy);
        }
        for (int i = 0; i < 4; i++) bit[i].clear(n);
        for (int i = 1; i <= n; i++) {
            int t = lower_bound(d + 1, d + dcnt + 1, a[i].y) - d;
            int xx = a[i].x, yy = a[i].y;
            ans += a[i].a * (xx - yy) * bit[0].query(n - t) - bit[1].query(n - t) + (xx - yy) * bit[2].query(n - t) - a[i].a * bit[3].query(n - t);
            bit[0].add(n - t + 1, 1), bit[1].add(n - t + 1, a[i].a * (xx - yy)), bit[2].add(n - t + 1, a[i].a), bit[3].add(n - t + 1, xx - yy);
        }
        for (int i = 0; i < 4; i++) bit[i].clear(n);
        cout << (long long)((ans / 2) % P) << "\n";
    }
    return 0;
}
posted @ 2025-07-28 20:25  forgotmyhandle  阅读(39)  评论(0)    收藏  举报