做题记录

5.7

题号 Link 类型
CF1083E The Fair Nut and Rectangles 斜率优化(板)
CF311B Cats Transport 斜率优化(板)
CF626F Group Projects Trick:将极差转为相邻两项差的和(板)
CF632 Thief in a Shop 价值维背包(也可以FFT),推荐
CF1107E Vasya and Binary String 小清新区间dp,有点ad-hoc,推荐
CF35D Broken robot 期望dp,但是高斯消元,推荐
CF1628D2 Game on Sum (Hard Version) 博弈论,但是从dp转为组合数学很自然,推荐
CF840C On the Bench 非常好的性质+插入dp
CF771D Bear and Company 有一点好玩的转化(从swap转为从最终状态计算贡献)
CF248E Piglet's Birthday 非常无趣的概率dp
CF703E Mishka and Divisors Trick :把整除转为 gcd,利用因数个数很小的条件来做,推荐
CF416E President's Path Trick:处理与边相关的信息时可以将边的信息转到点上

5.11

前面几天先咕咕咕。
Link
杂题

A

\(d(i)\) 表示 \(a_i - a_{i+\frac{n}{2}}\),那么发现 \(\Delta d(i) \in \{-2,0,2\}\)
\(d(1) = -d(\frac{n}{2}+1)\),所以可以二分。

C

很明显可以以 \(1\) 为根跑 dfs 那么我们可以计算出从 \(i\) 点出发的最长链和深度。

可以发现 \(x\)\(1\) 距离一半就是分界点,反方一定会选一条最长的路径走,然后直接一个一个往上跳就可以了。

D

好题。

首先抽象成一个矩阵,然后发现就是网格内有一部分的东西已经被填了,并且每行每列都有和的限制。

考虑将行列连边,用边来表示网格图上的点。那么边的数量是 \(O(n^2)\) 的,而只有 \(O(n)\) 个约束,非常恶心。

那么我们对于图上的一个联通分量,我们取它的生成树,其余边全部赋值为 \(0\),现在就只有 \(O(n)\) 个变量了,直接 dfs 完事。

E

没做出来。

首先末状态非常好求,但是考虑到对于步数相同的转化,最大值最大的一定更优秀,然后就把每一个元素转换步数排序过后从小到大分配权值即可。

F

贪心。

首先答案肯定有 \(\sum c_i\),那么考虑个 \(c_i\) 能干吗。

我们将 \(a_i\) 抽象成一个矩形,高度为 \(a_i\),那么我们从 \(i\) 免费(指在 \(c_i\) 的前置下)走到 \(j\) 当且仅当 \(a_j \leq a_i + c_i\)。否则我们要给 \(a_j-a_i-c_i\) 的代价。

那么我们直接对 \(a_i\) 排序,然后记录目前的 \(a_i+c_i\) 的最大值,然后直接扫一遍就可以了。

5.12

Link

A

考虑到值域为 \([0,m]\) 且一行中 \(m\) 个数互不相等,则在一行中只有一个数未出现。

\(dp_{i,j}\) 表示在 \(i\) 行中,\(j\) 未出现的方案数。

转移非常简单:

\[dp_{i,j}=\sum_{k=0}^{j+1}dp_{i-1,k}=dp_{i,j-1}+dp_{i-1,j+1} \]

显然这不能直接格路计数,但是当我们把 \(i\) 行的所有数点全部左移 \(i\) 格的时候,就可以做了。

B

容斥。

先弱化条件:抽象为现在有 \(m\) 个长度为 \(k+1\) 的块,要求放在长度为 \(n\) 轴上,问方案数。
很明显的插板,再乘一个 \(2\) 的幂次,就是弱化版的答案。

然而我们发现一棵树占据的块能选择向左倒或者向右倒,而这个东西对于我们本质需要计的数(就是位置)是弱相关的。

也就是说,对于一棵树能左右倒,需要做一个容斥。

直接上二项式反演,钦定有多少个两边都倒,直接做就行。

C

非常好的笛卡尔树+括号序列。

很明显对于这种有关区间最大值位置的东西可以上笛卡尔树,两个序列的同构等价于笛卡尔树的同构。

考虑值域范围的限制:由于值域最大为 \(m\), 所以对于任意节点,它向左走的最远距离不能超过 \(m\)

考虑对于 \(i\) 点,它的子树能放的数的范围为 \([1,v]\),那么我们可以让左儿子取 \([1,v-1]\),右儿子取 \([1,v]\)。这样一定能构造出一组解使得取遍 \([1,m]\) 的所有数。

那么现在就简单了,三度树转为括号序列,然后括号序列转前缀和,现在就是一个变量,每次可以增加一,也能减少一,但是不能超过 \(-1\)\(m+1\),求方案。

直接容斥即可。

5.13

Link

A

挺板的,只需要把 \(O(n^2)\) 的式子用二项式定理展开就可以。

B

记录一下对于每一个 \(i\),有多少个 \(j\) 有偏序关系,然后 dp+二项式反演就可以。

C~H

水题,咕咕咕。

5.14

还是昨天的比赛。

I

进行了 Hint 的使用,随后做出。

对于恰好问题但是又不好直接计数的,可以考虑二项式反演。

\(f(i)\) 表示至少有 \(i\) 个极大的的方案数。设 \(h(i)=(n-i)(m-i)(l-i)\)

(我们令 \(a(i,j)\) 表示 \(i\)\(j\) 次下降幂)

显然观察得到:\(f(i)=a(n,i) \times a(m,i) \times a(l,i) \times a(nml,h(i))\times g(i)\),其中 \(g(i)\) 是将 \(h(i)\) 个数按照偏序关系排好的方案数。

关键在于 \(g(i)\) 的求法。发现对于小的数,它对于其他格子的约束更严格,所以从小的往大的填。

观察发现,\(g(i)=\prod\limits_{j=1}^i\frac{(h(0)-h(j)-1)!}{(h(0)-h(j-1))!}\)

然后代回 \(f(i)\) 中,得到:\(f(i)=a(n,i) \times a(m,i) \times a(l,i) \times (nml)!\times \prod\limits_{j=1}^i\frac{1}{(nml-h(j))!}\)

答案为概率,所以还要除一个 \((nml)!\)

瓶颈在于求逆元。

放个代码吧:

#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 5e6 + 5;
const int Md = 998244353;
ll n, m, l, k, val[N], rev[N], revs[N];
ll qpow(ll a, ll b) {
    ll ret = 1;
    while (b) {
        if (b & 1) ret = (ret * a) % Md;
        a = (a * a) % Md;
        b >>= 1;
    }
    return ret;
}
ll F(ll x) {
    return (n - x) * (m - x) % Md * (l - x) % Md;
}
ll limit;
void init(void) {
    val[0] = 1;
    rev[0] = 1;
    ll sub = F(0);
    for (int i = 1; i <= limit; ++i) {
        val[i] = (sub + Md - F(i)) % Md;
        rev[i] = (rev[i - 1] * val[i]) % Md;
    }
    ll invs = qpow(rev[limit], Md - 2);
    for (int i = limit; i; --i) {
        rev[i] = invs * rev[i - 1] % Md;
        invs = (invs * val[i]) % Md;
    }
}
void pre(void) {
    revs[0] = 1;
    for (int i = 1; i < N; ++i)
        revs[i] = (revs[i - 1] * i) % Md;
    ll invs = qpow(revs[N - 1], Md - 2);
    for (int i = N - 1; i > 0; --i) {
        revs[i] = invs * revs[i - 1] % Md;
        invs = (invs * i) % Md;
    }
}
void solve(void) {
    cin >> n >> m >> l >> k;
    limit = min({ n, m, l });
    init();
    ll C = 0, S = 1, ret = 0;
    for (int i = 1; i <= limit; ++i) {
        S = (S * F(i - 1) % Md * rev[i]) % Md;
        if (i == k) C = 1;
        if (i > k) C = (Md - C * i % Md * revs[i - k] % Md) % Md;
        ret = (ret + C * S % Md) % Md;
    }
    cout << ret << '\n';
}
int main() {
    FASTIO;
    pre();
    int t;
    cin >> t;
    while (t--) solve();
    return 0;
}

K

好像很牛的样子。

没做出来。

考虑刻画这个括号序列的价值,可以发现,我们肯定可以在一个分界点把左边的右括号都删去,然后把右边的所有左括号都删掉,然后价值就可以刻画出来了。

我们直接在分界点上计数,设在当前分界点处左侧有 \(l\) 个左括号,有 \(x\) 个问号,右侧有 \(r\) 个左括号,\(y\) 个问号。

显然我们有贡献:\(\sum\limits_{i=0}^x(l+x)\binom{x}{i}\binom{y}{l+i-r}\)

然后化简一下式子,再用一下 Vandermonde 卷积,得到 :

\[l\binom{x+y}{y+r-l}+x\binom{x+y-1}{y+r-l-1} \]

这样就可以 \(O(n)\) 计算了。

也是给一个代码:

#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 4e6 + 5;
const int Md = 998244353;
ll fac[N], rev[N];
int pre[2][N], suf[2][N];
ll qpow(ll a, ll b) {
    ll ret = 1;
    while (b) {
        if (b & 1) ret = (ret * a) % Md;
        a = (a * a) % Md;
        b >>= 1;
    }
    return ret;
}
void init(void) {
    fac[0] = rev[0] = 1;
    for (int i = 1; i < N; ++i) {
        fac[i] = (fac[i - 1] * i) % Md;
        rev[i] = (rev[i - 1] * qpow(i, Md - 2)) % Md;
    }
}
ll C(int n, int m) {
    if (n < m || n < 0) return 0;
    return fac[n] * rev[m] % Md * rev[n - m] % Md;
}
int main() {
    FASTIO;
    init();
    string s;
    cin >> s;
    s = " " + s;
    for (int i = 1; i < s.size(); ++i) {
        pre[0][i] = pre[0][i - 1] + (s[i] == '?');
        pre[1][i] = pre[1][i - 1] + (s[i] == '(');
    }
    for (int i = s.size() - 1; i; --i) {
        suf[0][i] = suf[0][i + 1] + (s[i] == '?');
        suf[1][i] = suf[1][i + 1] + (s[i] == ')');
    }
    ll ret = 0;
    for (int i = 1; i < s.size(); ++i) {
        ll add = pre[1][i - 1] * C(pre[0][i - 1] + suf[0][i], suf[1][i] + suf[0][i] - pre[1][i - 1]) % Md;
        add = (add + pre[0][i - 1] * C(pre[0][i - 1] + suf[0][i] - 1, suf[1][i] + suf[0][i] - pre[1][i - 1] - 1) % Md) % Md;
        ret = (ret + add) % Md;
    }
    cout << ret << '\n';
    return 0;
}

posted @ 2026-05-07 15:53  To_string  阅读(10)  评论(0)    收藏  举报