首页 > 其他分享 >20241111

20241111

时间:2024-11-11 20:30:47浏览次数:1  
标签:20241111 tmp r2 int l2 l1 push

Happy Birthday Elysia !

T1

很有味道的题目

\(dp_i\) 表示到 \(a\) 的第 \(i\) 个数,最多能到 \(b\) 的哪一个数。向后转移能够给到的是一个值域的后缀,离散化后 BIT 维护即可。

代码
#include <iostream>
#include <algorithm>
#define lowbit(x) ((x) & (-(x)))
#define int long long
using namespace std;
int n;
int a[1000005], b[1000005];
int dp[1000005];
struct BIT {
    int bit[1000005];
    void add(int x, int y) { for (; x <= 1000000; x += lowbit(x)) bit[x] = max(bit[x], y); }
    int query(int x) {
        int ret = 0;
        for (; x; x -= lowbit(x)) ret = max(ret, bit[x]);
        return ret;
    }
} bit;
int d[1000005], dcnt;
signed main() {
    freopen("were.in", "r", stdin);
    freopen("were.out", "w", stdout);
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i], d[i] = a[i];
    for (int i = 1; i <= n; i++) cin >> b[i];
    sort(d + 1, d + n + 1);
    dcnt = unique(d + 1, d + n + 1) - d - 1;
    int ans = 0;
    for (int i = 0; i <= n; i++) {
        dp[i] = bit.query(lower_bound(d + 1, d + dcnt + 1, a[i]) - d);
        int x = upper_bound(d + 1, d + dcnt + 1, a[i] * b[dp[i]]) - d;
        bit.add(x, dp[i] + 1);
        ans = max(ans, dp[i]);
    }
    cout << ans << "\n";
    return 0;
}

T2

东百演说家

这个题比上个题有味道多了。

首先有 \(\begin{cases} xd + y \equiv a + d \\ yd + x \equiv b + d \end{cases} \pmod w\)。当 \(w > 2\) 时,加减可得其等价于 \(\begin{cases} (x + y)(d + 1) \equiv a + b + 2d \\ (x - y)(d - 1) \equiv a - b \end{cases} \pmod w\)。\(w = 2\) 时则平凡。然后考虑 \(d \pm 1\) 的逆元在模 \(w\) 意义下的存在性。若两逆元均存在,则等价于限定了 \(x\) 和 \(y\) 各自模 \(w\) 的余数。把两边的情况数乘起来即可。若两逆元都不存在,则没有任何限制,情况数为值域平方。然后是只有一个逆元不存在的情况,相当于限定了模意义下的和或差,要求方案数。枚举其中一个数,则另一个数可以确定,方案数为值域内模 \(w\) 同余于它的数的个数。容易发现这里的个数只有两种取值,于是可以求出界点然后分讨,然后就做完了。复杂度为 \(\mathcal{O}(T\log w)\),瓶颈在于求逆元。

代码
#include <iostream>
#define int long long
using namespace std;
inline int qpow(int x, int y, int P) {
    int ret = 1;
    while (y) {
        if (y & 1) 
            ret = ret * x % P;
        y >>= 1;
        x = x * x % P;
    }
    return ret;
}
inline int inv(int x, int P) { return qpow(x, P - 2, P); }
int f(int l, int r, int x, int P) {
    l -= x, r -= x;
    if (r < 0) 
        return 0;
    if (l <= 0) 
        return r / P + 1;
    else 
        return r / P - (l - 1) / P;
}
int calc(int l1, int r1, int l2, int r2) {
    if (l1 <= l2 && r2 <= r1) 
        return r2 - l2 + 1;
    if (l2 <= l1 && r1 <= r2) 
        return r1 - l1 + 1;
    if (l1 > l2) 
        swap(l1, l2), swap(r1, r2);
    return max(0ll, r1 - l2 + 1);
}
inline int M(int x, int y) { return x % y == 0 ? y : x % y; }
int Solve(int m, int d, int w, int a, int b) {
    int lim = min(m, d), ans = 0, ls, ld;
    // for (int i = 1; i <= lim; i++) {
    //     for (int j = 1; j <= lim; j++) 
    //         ans += ((i * d + j) % w == (a + d) % w) && ((j * d + i) % w == (b + d) % w);
    // }
    // return ans;
    if (w == 2) {
        // x = 0, y = 0
        if (0 == (a + d) % w && 0 == (b + d) % 2) 
            ans += (lim / 2) * (lim / 2);
        // x = 0, y = 1
        if (1 == (a + d) % w && d % 2 == (b + d) % 2) 
            ans += (lim / 2) * ((lim + 1) / 2);
        // x = 1, y = 0
        if (d % 2 == (a + d) % w && 1 == (b + d) % 2) 
            ans += ((lim + 1) / 2) * (lim / 2);
        // x = 1, y = 1
        if ((d + 1) % 2 == (a + d) % w && (d + 1) % 2 == (b + d) % 2) 
            ans += ((lim + 1) / 2) * ((lim + 1) / 2);
        return ans;
    }
    if ((d + 1) % w == 0) {
        if ((a + b + 2 * d) % w != 0) 
            return 0;
        ls = -1;
    } else 
        ls = (a + b + 2 * d) * inv(d + 1, w) % w;

    if ((d - 1) % w == 0) {
        if ((a - b) % w != 0) 
            return 0;
        ld = -1;
    } else {
        ld = (a - b) * inv(d - 1, w) % w;
        ld = (ld + w) % w;
    }
    if (ls == -1 && ld == -1) 
        return lim * lim;
    // cout << "sdf\n";
    if (ls == -1) {
        // for (int x = 0; x < w; x++) 
        //     ans += f(1, lim, x, w) * f(1, lim, (x + ld) % w, w);
        int p = M(lim, w), a = (lim - 1) / w + 1, b = a - 1, c = 0, r = w;
        int ll = M(1 + ld, w), rr = M(p + ld, w);
        (ll <= rr) ? (c = calc(1, p, ll, rr)) : (c = calc(1, p, ll, w) + calc(1, p, 1, rr));
        r -= c;
        ans += c * a * a;
        if (p != w) {
            ++p;
            ll = M(p + ld, w), rr = M(w + ld, w);
            (ll <= rr) ? (c = calc(p, w, ll, rr)) : (c = calc(p, w, ll, w) + calc(p, w, 1, rr));
            r -= c;
            ans += c * b * b;
        }
        ans += r * a * b;
        return ans;
    } else if (ld == -1) {
        // for (int x = 0; x < w; x++) 
        //     ans += f(1, lim, x, w) * f(1, lim, (ls - x + w) % w, w);
        int p = M(lim, w), a = (lim - 1) / w + 1, b = a - 1, ll = M(w + ls - p, w), rr = M(w + ls - 1, w), r = w, c;
        (ll <= rr) ? (c = calc(1, p, ll, rr)) : (c = calc(1, p, ll, w) + calc(1, p, 1, rr));
        r -= c;
        ans += c * a * a;
        if (p != w) {
            ++p;
            ll = M(w + ls - w, w), rr = M(w + ls - p, w);
            (ll <= rr) ? (c = calc(p, w, ll, rr)) : (c = calc(p, w, ll, w) + calc(p, w, 1, rr));
            r -= c;
            ans += c * b * b;
        }
        ans += r * a * b;
        return ans;
    } else {
        // cout << "asdf\n";
        // cout << ls << " " << ld << "\n";
        int x = (ls + ld) * inv(2, w) % w;
        int y = (ls - ld + w) * inv(2, w) % w;
        // cout << x << " " << y << "\n";
        return f(1, lim, x, w) * f(1, lim, y, w);
    }
    return 0;
}
signed main() {
    freopen("speech.in", "r", stdin);
    freopen("speech.out", "w", stdout);
    int tc;
    cin >> tc;
    while (tc--) {
        int m, d, w, a, b;
        cin >> m >> d >> w >> a >> b;
        cout << Solve(m, d, w, a, b) << "\n";
    }
    return 0;
}

T3

不朽之蜍

应该想到求的其实是所有(数)字牌被第一次摸到的时间的最大值。可以考虑 min-max 容斥。那么现在我们钦定 \(n - k\) 张字牌变成鬼牌,要求此时一轮游戏终结时的期望轮数。可以列出式子 \(f(k) = \sum\limits_{i = 1}^{k} \frac{k^{\underline{i}}}{(n + m)^{\underline{i}}}\)。推一下式子会发现这个就等于 \(\frac{k}{n + m - k + 1}\)。然后在一轮结束时抽到的可能不是字牌变的鬼牌,因此要乘上 \(\frac{m + k}{k}\) 表示期望这么多轮结束后能抽到字牌变的鬼牌而结束。再套上 min-max 容斥的式子即可。

代码
#include <iostream>
#define int long long
using namespace std;
const int P = 19260817;
int f[1000005];
int fac[2000005], ifac[2000005], inv[2000005];
void Cpre(int n) {
    fac[0] = fac[1] = ifac[0] = ifac[1] = inv[0] = inv[1] = 1;
    for (int i = 2; i <= n; i++) {
        fac[i] = fac[i - 1] * i % P;
        inv[i] = (P - P / i) * inv[P % i] % P;
        ifac[i] = ifac[i - 1] * inv[i] % P;
    }
}
inline int C(int n, int m) { return fac[n] * ifac[m] % P * ifac[n - m] % P; }
int n, m, ans;
signed main() {
    freopen("toad.in", "r", stdin);
    freopen("toad.out", "w", stdout);
    Cpre(2000000);
    cin >> n >> m;
    for (int i = 0; i < n; i++) f[i] = i * inv[n + m - i + 1] % P;
    for (int i = 1; i <= n; i++) {
        int x = (f[n - i] + 1) * (m + i) % P * inv[i] % P * C(n, i) % P;
        i & 1 ? (ans += x) : (ans -= x);
    }
    cout << (ans % P + P) % P << "\n";
    return 0;
}

T4

大战杀马特

容易想到超级钢琴套路。在这个题中,我们考虑把一维变成二维,每次求出一个矩形中的最优决策,然后分出更多决策。这里我们能算的是两维的区间完全相同和互不相交的情况,于是只需要保证每次分出的矩形都符合这两个条件即可。

代码
#include <iostream>
#include <cassert>
#include <queue>
#include <array>
#define int long long
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f;
struct node {
    array<int, 2> mx, mn;
    array<int, 3> ans;
    int tg;
    void tag(int v) { mx[0] += v, mn[0] += v, tg += v; }
} T[400005];
int n, m;
int a[100005];
node operator+(node a, node b) {
    node c;
    c.mx = max(a.mx, b.mx), c.mn = min(a.mn, b.mn);
    c.ans = max((array<int, 3>) { a.mx[0] - b.mn[0], a.mx[1], b.mn[1] }, max(a.ans, b.ans));
    c.tg = 0;
    return c;
}
struct Segment_Tree {
    void pushdown(int o) {
        if (!T[o].tg) 
            return;
        T[o << 1].tag(T[o].tg);
        T[o << 1 | 1].tag(T[o].tg);
        T[o].tg = 0;
    }
    void Build(int o, int l, int r) {
        if (l == r) {
            T[o].mx[0] = T[o].mn[0] = a[l];
            T[o].mx[1] = T[o].mn[1] = T[o].ans[1] = T[o].ans[2] = l;
            return;
        }
        int mid = (l + r) >> 1;
        Build(o << 1, l, mid);
        Build(o << 1 | 1, mid + 1, r);
        T[o] = T[o << 1] + T[o << 1 | 1];
    }
    void Add(int o, int l, int r, int L, int R, int v) {
        if (L <= l && r <= R) 
            return T[o].tag(v);
        pushdown(o);
        int mid = (l + r) >> 1;
        if (L <= mid) 
            Add(o << 1, l, mid, L, R, v);
        if (R > mid) 
            Add(o << 1 | 1, mid + 1, r, L, R, v);
        T[o] = T[o << 1] + T[o << 1 | 1];
    }
    node Query(int o, int l, int r, int L, int R) {
        if (L <= l && r <= R) 
            return T[o];
        pushdown(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);
    }
} seg;
inline array<int, 3> K1(int l, int r) { return seg.Query(1, 1, n, l, r).ans; }
struct X {
    int l1, r1, l2, r2;
    array<int, 3> v;
};
inline bool operator<(X a, X b) { return a.v[0] < b.v[0]; }
priority_queue<X> q;
void push(int l1, int r1, int l2, int r2) {
    if (l1 == l2) 
        q.push((X) { l1, r1, l2, r2, K1(l1, r2) });
    else {
        array<int, 2> a = seg.Query(1, 1, n, l1, r1).mx;
        array<int, 2> b = seg.Query(1, 1, n, l2, r2).mn;
        q.push((X) { l1, r1, l2, r2, (array<int, 3>) { a[0] - b[0], a[1], b[1] } });
    }
}
int Query(int L, int R, int k) {
    while (!q.empty()) q.pop();
    push(L, R, L, R);
    int ret = 0;
    while (k--) {
        assert(q.size());
        X tmp = q.top();
        q.pop();
        ret += tmp.v[0];
        int x = tmp.v[1], y = tmp.v[2];
        if (tmp.l1 == tmp.l2) {
            int l = tmp.l1, r = tmp.r2;
            if (l != x) {
                push(l, x - 1, l, x - 1);
                push(l, x - 1, x, r);
            }
            if (x != y) 
                push(x, x, x, x);
            if (x + 1 < y) 
                push(x, x, x + 1, y - 1);
            if (y != r) 
                push(x, x, y + 1, r);
            if (x != r) 
                push(x + 1, r, x + 1, r);
        } else {
            if (x != tmp.l1) 
                push(tmp.l1, x - 1, tmp.l2, tmp.r2);
            if (y != tmp.l2) 
                push(x, x, tmp.l2, y - 1);
            if (y != tmp.r2) 
                push(x, x, y + 1, tmp.r2);
            if (x != tmp.r1) 
                push(x + 1, tmp.r1, tmp.l2, tmp.r2);
        }
    }
    return ret;
}
signed main() {
    freopen("smart.in", "r", stdin);
    freopen("smart.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    seg.Build(1, 1, n);
    while (m--) {
        int op, k, x, y;
        cin >> op >> x >> y >> k;
        if (op == 2) 
            cout << Query(x, y, k) << "\n";
        else 
            seg.Add(1, 1, n, x, y, k);
    }
    return 0;
}

在第二题上花的时间太长了。还是分讨能力不行。

标签:20241111,tmp,r2,int,l2,l1,push
From: https://www.cnblogs.com/forgotmyhandle/p/18540529

相关文章