闲话 23.3.17

闲话

今日闲话【碎片】
打每日刷新的副本时可能随机掉落,当且仅当今日有模拟赛时掉落概率不为 0。

怎么这几天的闲话阅读量单调递减呢?
没人想看我的垃圾博客;;

模拟赛

口胡万岁!

T1 \(%计数最大向量不超过 n 的异或空间。\)
考虑把空间和基对应,为方便,我们只计算最简阶梯矩阵。同样的,一组基的张成中最大向量就是这组基的加和,这启发我们作数位 dp。
\(f(i, j, b)\) 表示填到第 \(i\) 位,基大小为 \(j\),卡上界当 \([b]\) 为真。设当前位的上界是 \(\text{bnd}\),考虑是否卡界/添加主元可以得到

\[f(i, j, b) \to f(i - 1, j + 1, b) \quad (\text{bnd} = 1) \]

\[2^{j - 1}\times f(i, j, b) \to f(i - 1, j, b) \quad(j > 0) \]

\[\max(2^{j - 1}, 1)\times f(i, j, b) \to f(i - 1, j, b\land \text{bnd} = 0) \]

总复杂度 \(O(\log^2 n)\)

code
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
template<typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
template<typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 45;
int n, f[N][N][2];

int main() {
	iot; cin >> n;
    f[30][0][1] = 1;
    pre(i,30,1) rep(j,0,30) rep(k,0,1) if (f[i][j][k]) {
		int top = k ? (n >> (i - 1) & 1) : 1;
		addi(f[i - 1][j][k && 0 == top], (1ll << max(j - 1, 0)) * f[i][j][k] % mod);
		if (top == 1) {
			addi(f[i - 1][j + 1][k], f[i][j][k]);
			if (j > 0) addi(f[i - 1][j][k], (1ll << j - 1) * f[i][j][k] % mod);
		} 
	}
    int ans = 0;
	rep(i,0,30) addi(ans, f[0][i][0], f[0][i][1]);
	cout << ans << '\n';
}

T2 \(%计数第一棵树上是联通块、第二棵树上是链的点标号集合。\)
首先有个假做法是点分治并用可撤销并查集拼起来,因为两条链可以拼合。
联通块的充要条件是点集 \(-\) 生成边集 \(=1\),因为是树。
考虑把一条 \(T_2\) 上路径 \(u\to v\) 映射到二维平面上的一个点 \((\text{dfn}[u],\text{dfn}[v])\)
考虑 \(T_1\) 的每个元素对这个平面的贡献。对一条边 \((u, v)\),包含它的所有路径组成了 \(O(1)\) 个矩形,因为两个端点的可行区间在 dfn 序上是连续(分段连续)的。对一个点 \(u\) 枚举一下出边,能发现有 \(O(出边个数)\) 个矩形。
因此我们就把这 \(T_1\) 上每个联通块对链的贡献转化成了矩形上的 \(O(n)\) 次加减,点加边减。统计平面上值为 \(1\) 的点数即可,这可以通过线段树维护最小值和数量,作扫描线得到。
总时间复杂度 \(O(n\log n)\)

code
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
template<typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
template<typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 1e5 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, t1, t2, u[N], v[N]; 
vector<tuple<int, int, int>> vc[N];
ll ans;
vi g[N];

int siz[N], dep[N], dfn[N], stp, st[N][21];
void dfs(int u, int ft) {
    siz[u] = 1, dep[u] = dep[ft] + 1, dfn[u] = ++ stp, st[u][0] = ft;
    rep(j,1,20) st[u][j] = st[st[u][j - 1]][j - 1];
    for (auto v : g[u]) if (v != ft) dfs(v, u), siz[u] += siz[v];
}
inline int getson(int u, int v) {
    pre(j,20,0) if (dfn[st[u][j]] > dfn[v]) u = st[u][j];
    return u;
}

struct sgt {
    #define ls (p << 1)
    #define rs (p << 1 | 1)
    #define mnv(p) seg[p].mnv
    #define cnt(p) seg[p].cnt
    #define lzy(p) seg[p].lzy

    struct node {
        int mnv, cnt, lzy;
    } seg[N << 2];

    inline void ps_p(int p) {
        mnv(p) = min(mnv(ls), mnv(rs)); 
        cnt(p) = cnt(ls) * (mnv(ls) == mnv(p)) + cnt(rs) * (mnv(rs) == mnv(p));
    }
    inline void ps_d(int p) {
        if (!lzy(p)) return;
        mnv(ls) += lzy(p), lzy(ls) += lzy(p);
        mnv(rs) += lzy(p), lzy(rs) += lzy(p);
        lzy(p) = 0;
    }

    void build(int p, int l, int r) {
        mnv(p) = lzy(p) = 0, cnt(p) = 1; 
        if (l == r) return;
        int mid = l + r >> 1;
        build(ls, l, mid), build(rs, mid + 1, r);
        ps_p(p);
    }
    void update(int p, int l, int r, int L, int R, int va) {
        if (L <= l and r <= R) return static_cast<void>(mnv(p) += va, lzy(p) += va);
        ps_d(p); int mid = l + r >> 1;
        if (L <= mid) update(ls, l, mid, L, R, va);
        if (mid < R) update(rs, mid + 1, r, L, R, va);
        ps_p(p);
    }
} Tr;

inline void insert(int l1, int r1, int l2, int r2, int va) {
    if (l1 <= r1 and l2 <= r2) 
        vc[l1].eb(l2, r2, va), vc[r1 + 1].eb(l2, r2, -va);
}

signed main() {
    iot; 
    multi {
        cin >> n;
        rep(i,2,n) cin >> u[i] >> v[i];
        rep(i,2,n) cin >> t1 >> t2, g[t1].eb(t2), g[t2].eb(t1);
        dfs(1, 0);
        rep(u,1,n) {
            for (auto v : g[u]) {
                if (v == st[u][0]) insert(1, dfn[u] - 1, dfn[u], dfn[u] + siz[u] - 1, 1), insert(dfn[u] + siz[u], n, dfn[u], dfn[u] + siz[u] - 1, 1);
                else insert(dfn[v], dfn[v] + siz[v] - 1, 1, dfn[v] - 1, 1), insert(dfn[v], dfn[v] + siz[v] - 1, dfn[v] + siz[v], n, 1);
            } insert(dfn[u], dfn[u], 1, n, 1);
        }
        rep(i,2,n) {
            if (dfn[u[i]] < dfn[v[i]]) swap(u[i], v[i]);
            if (dfn[u[i]] < dfn[v[i]] + siz[v[i]]) {
                int t = getson(u[i], v[i]);
                insert(dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, 1, dfn[t] - 1, -1), insert(dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, dfn[t] + siz[t], n, -1);
                insert(1, dfn[t] - 1, dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, -1), insert(dfn[t] + siz[t], n, dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, -1);
            } else {
                insert(dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, dfn[v[i]], dfn[v[i]] + siz[v[i]] - 1, -1);
                insert(dfn[v[i]], dfn[v[i]] + siz[v[i]] - 1, dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, -1);
            }
        }
        Tr.build(1, 1, n);
        rep(i,1,n) {
            for (auto [l, r, va] : vc[i]) Tr.update(1, 1, n, l, r, va);
            // cout << Tr.seg[1].mnv << ' ' << Tr.seg[1].cnt << '\n';
            if (Tr.seg[1].mnv == 1) ans += Tr.seg[1].cnt;
        }
        cout << (ans + n) / 2 << '\n';
        rep(i,1,n) g[i].clear(), vc[i].clear(); ans = stp = 0;
    }
}

T3 \(%给字符串 S。计数最少的间隔符加入数,使得:给 q 次一个字符串 S 的子串,加入间隔符后不存在这个子串。\)
考虑确定一个询问子串的情况。我们可以首先找到所有出现位置 \(S = \{ [l_i, r_i]\}\),随后可以贪心地算出最少加入数。具体地,我们每次找最左边的区间,在他右端点前放一个间隔符,并删除所有包含这间隔符的区间即可。这个上线段树是 \(O(|S| \log n)\) 的,用 radix sort + umap 是 \(O(|S|)\) 的。
第二种做法具体如下:
设当前拿到的区间集合是 \(S = \{ [l_i, r_i]\}\),这是左值+右值各 \(|S|\) 个。首先把 \(2|S|\) 个值 radix sort,从小到大扫值域。如果当前值是左值,把对应区间加入一个 umap。otherwise 给 umap 里所有值打标记并清空 umap,若 umap 清空前不空就答案加 \(1\);如果这个右值对应的左值被打了标记就不清空,略过即可。容易发现这做法是 \(O(|S|)\) 的。
我们自然想到了根号分治。
对于 \(\le B\) 的询问,我们发现可能的总串数是 \(O(nB)\) 的,可以 \(O(nB)\) 地预处理。具体地,我们对每个 \(\le B\) 的长度 \(k\),找到 \(n - k\) 个可能的串,每次拿出所有相等的串作如上的贪心。这样总复杂度是 \(O(nB) - O(1)\) 的。
对于 \(\ge B\) 的询问,如果暴力按上面做法做很可能劣化到单次 \(O(n)\)。但是从上面的想法我们能看出一个性质:若长度 \(\ge B\),清空 umap 的次数不超过 \(n / B\)。这告诉我们,上线段树的总时间复杂度是 \(O(n\log n /B)\) 的。这样我们就只需要快速找到所有右端点。
不难发现这右端点本质上是询问串的 endpos,因此得到原串的后缀链接树后即可在 \(O(n\log n)\) 的复杂度内通过 SAM 上线段树合并的方式得到每个询问串的 endpos 集合。随后在 SAM 上查出串,用线段树做贪心即可。
\(q = O(n)\),总时间复杂度为 \(O(nB + n\log n / B)\),取 \(O(\sqrt{n\log n})\) 得到时间复杂度 \(O(n\sqrt{n\log n})\)

code
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
template<typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
template<typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 2e5 + 5, B = 450;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, m, sqr, ret[N][B];
char S[N];
vi vec[N], g[N];

struct sgt {
	#define ls(p) seg[p].l
	#define rs(p) seg[p].r

	int mlc, rt[N];
	struct node { 
		int l, r; 
	} seg[N << 6]; 

	void update(int& p, int l, int r, int x) {
		if (!p) p = ++ mlc;
		if (l == r) return;
		int mid = l + r >> 1;
		if (x <= mid) update(ls(p), l, mid, x);
		else update(rs(p), mid + 1, r, x);
	} 
	
	int merge(int x, int y) {
		if (!x or !y) return x | y;
		int ret = ++mlc;
		ls(ret) = merge(ls(x), ls(y));
		rs(ret) = merge(rs(x), rs(y));
		return ret;
	}

	int query(int p, int l, int r, int x) {
		if (!p) return 0;
		if (l == r) return l;
		int mid = l + r >> 1;
		if (x > mid) return query(rs(p), mid + 1, r, x);
		int ret = query(ls(p), l, mid, x);
		if (ret) return ret;
		return query(rs(p), mid + 1, r, x);
	}
} Tr;

int son[N][26], link[N], len[N], pos[N], mlc = 1, lst = 1;
void extend(int c, int id) {
	int now = ++ mlc, p = lst;
	len[now] = len[lst] + 1;
	while (p and !son[p][c]) 
		son[p][c] = now, p = link[p];
	if (p == 0) link[now] = 1;
	else {
		int q = son[p][c];
		if (len[p] + 1 == len[q]) link[now] = q;
		else {
			int kage = ++ mlc;
			len[kage] = len[p] + 1, link[kage] = link[q];
			memcpy(son[kage], son[q], sizeof(son[q]));
			while (p and son[p][c] == q) 
				son[p][c] = kage, p = link[p];
			link[q] = link[now] = kage;
		}
	} 
	pos[id] = lst = now;
}

int fa[N][21];
void dfs(int u) {
	rep(i,1,20) fa[u][i] = fa[fa[u][i - 1]][i - 1];
	for(int v : g[u]) {
		dfs(v);
		if (len[u] > sqr) Tr.rt[u] = Tr.merge(Tr.rt[u], Tr.rt[v]);
	}
}
int find(int u, int d) {
	pre(i,20,0) if (len[ fa[u][i] ] >= d) u = fa[u][i]; return u;
}

signed main () {
	iot; 
	cin >> n >> m >> S + 1;
	sqr = sqrt(n * log(n));
	rep(i,1,n) extend(S[i] - 'a', i);
	rep(i,1,mlc) fa[i][0] = link[i], g[link[i]].eb(i);
	rep(i,1,n) Tr.update(Tr.rt[pos[i]], 1, n, i);
	rep(i,1,n) for(int u = find(pos[i], sqr); u; u = link[u]) vec[u].eb(i);
	dfs(1);
	rep(i,1,mlc) rep(d,len[link[i]] + 1,min(len[i],sqr)) {
		int p = 1;
		for(auto it : vec[i]) if (it - d + 1 >= p) ++ret[i][d], p = it;
	}

	rep(i,1,m) {
		int l, r;
		cin >> l >> r;
		if (l == r)  { cout << -1 << "\n"; continue; }
		int d = r - l + 1, u = find(pos[r], d);
		if (d <= sqr) cout << ret[u][d] << "\n";
		else {
			int p = 1, ret = 0;
			while(p + d - 1 <= n and (p = Tr.query(Tr.rt[u], 1, n, p + d - 1))) ++ ret; 
			cout << ret << "\n";
		}
	}
}

杂题

CF1278F

\(m\) 张牌,其中一张是王牌。现在你执行 \(n\) 次如下操作:洗牌后查看第一张牌是什么。

\(x\) 为洗牌后第一张牌为王牌的次数,现在假设洗牌时 \(m!\) 种牌的排列出现的概率均相等,求 \(x^k\) 的期望值。

\(1\le n, m \le 998244532, 1\le k\le 10^5\)

考虑出现王牌的概率是 \(m^{-1}\),我们可以简单地枚举出现王牌的次数以及对答案的贡献。答案就是

\[\begin{aligned} & \sum_{i\ge 0} \binom{n}{i} i^k m^{-i} (1 - m^{-1}) ^{n - i} \\ = \ & \frac{1}{m^n} \sum_{i\ge 0} \binom{n}{i} i^k (m - 1)^{n - i} \\ = \ & \frac{1}{m^n} \sum_{i\ge 0} \sum_{j = 0}^k j!\binom{n}{i} \binom{i}{j}{k\brace j} (m - 1)^{n - i} \\ = \ & \frac{1}{m^n} \sum_{i\ge 0} \sum_{j = 0}^k j!\binom{n}{j} \binom{n - j}{i - j}{k\brace j} (m - 1)^{n - i} \\ = \ & \frac{1}{m^n} \sum_{i\ge 0} \sum_{j = 0}^k j! \binom{n}{j} \binom{n - j}{i - j}{k\brace j} (m - 1)^{n - i} \\ = \ & \frac{1}{m^n} \sum_{j = 0}^k j! \binom{n}{j} {k\brace j} \sum_{i\ge 0}\binom{n - j}{n - i} (m - 1)^{n - i} \\ = \ & \frac{1}{m^n} \sum_{j = 0}^k j! \binom{n}{j} {k\brace j} m^{n - j} \\ = \ & \sum_{j = 0}^k {k\brace j} n^{\underline j} m^{- j} \end{aligned}\]

可以 \(O(k\log k)\) 地求得。

Submission.



AGC040F

有两个棋子初始点都在坐标 \(0\),两个棋子之间没有区别,总共要进行 \(n\) 次操作,每次操作是如下操作其中之一:

  1. 选择一个棋子向前移动一步。

  2. 选择位置较后的那个棋子,直接移动到位置较前的那个棋子的位置。

计数 \(n\) 次操作后两个棋子分别在位置 \(a,b\) 的方案,对 \(998244353\) 取模。两种方案是相同的,当且仅当两个棋子在每一步的坐标都是相同的(注意不是每一步的操作都相同)。

\(1\le a\le b\le n \le 10^7\)

又到了今天的生成函数时间!
什么?你看这题面不像?我也看着不像。

但是确实可以。
约定一个高维生成函数 \(F(x_1, \dots, x_k)\) 的第 \(x_1 \dots x_k\) 项记为 \(F[x_1, \dots, x_k]\)
欸,你别害怕啊!\(%完全疯狂!\)

你看 2. 操作不是很好刻画。那就不刻画了,先找性质。
可以发现虽然 2. 操作执行次数不定,但是其对两个棋子的影响是一样的,这也就让我们可以先只考虑 1. 操作(可以为空),再考虑加一个 \(2\) 操作的影响,用 \(\text{SEQ}\) 构造拼起来。

现在只讨论 1. 操作。首先扔到二维平面上,然后就是只能向右向上走,问有多少种走法从 \((0, 0)\)\((x, y) \text{ s.t. } x < y\) 且不经过 \(y = x\);这是由于 \((x, y)\) 在施 2. 操作后都可以到达 \((y, y)\),本质相同。

转二维平面路径为括号序列。
我们可以发现,如果设 O 为合法括号匹配,答案一定形如 O(O(...O(O,这启发我们首先计数 (O,再拼出答案。设卡特兰数的 ogf 是 \(C(z)\),并让 \(u\) 表示左括号的占位元,\(v\) 表示右括号的占位元,则形如 (O 的括号序列的组合类 \(\mathcal O_1\) 就能表为 \(v\left(1 + \sum_{i} C[i]u^iv^i\right) = v(1 + C(uv))\)
设只讨论 1. 操作的操作序列的组合类是 \(\mathcal G\),则 \(\mathcal G = \text{SEQ}(\mathcal O_1)\),也就是

\[G(u, v) = \frac{1}{1 - v(1 + C(uv))} \]

我们要算答案,首先要用 \(u, v\) 分别表示两个棋子的坐标,还要加一维 \(w\) 表示用的次数。设答案的组合类是 \(\mathcal F\),ogf 是 \(F(u, v, w)\)
可以发现,若记一系列极长的 1. 操作为 \(C\),一次 2. 操作为 \(S\),则最终操作序列一定形如 CSCS...CSC,并且最后一定停止在 \((a, b)\)。我们不妨考虑先构造 CS,再加入一个 C。为方便,记他们的组合类是 \(\text{CS}, \text{C}\)
CS 就考虑一次 C 到定点 \((a, b)\),再冲到 \((b, b)\) 的贡献。这过程的方案数是 \(G[a, b]\),对 \(u, v\) 的贡献都是 \(b\),对 \(w\) 的贡献是 \(a + b + 1\)。也就是

\[\sum_{(a, b)} G[a, b] u^bv^bw^{a + b + 1} = w\sum_{(a,b)} G[a,b]w^a(uvw)^b = wG(w, uvw) \]

C 的话类似,我们只需要考虑前半部分的贡献。这过程的方案数是 \(G[a, b]\),对 \(u\) 的贡献是 \(a\)\(v\) 的贡献是 \(b\),对 \(w\) 的贡献是 \(a + b\)。也就是

\[\sum_{(a, b)} G[a, b] u^av^bw^{a + b} = \sum_{(a,b)} G[a,b](uw)^a(vw)^b = G(uw, vw) \]

我们知道 \(\mathcal F= \text{SET}(\text{CS}) \times \text{C}\),也就是

\[F(u,v,w) = \frac{G(uw,vw)}{1 - wG(w, uvw)} \]

答案就是 \([u^av^bw^n]F\)

考虑分母 \(G\) 的第二维传进去了 \(uvw\),我们不妨先把他干掉。设 \(x = uv, y = v\),我们要的就是

\[\begin{aligned} &[u^av^bw^n]\frac{G(uw,vw)}{1 - wG(w, uvw)} \\ = \ & [x^ay^{b - a}w^n]\frac{G(xw/y,yw)}{1 - wG(w, xw)} \\ = \ & [x^aw^n]\frac{1}{1 - wG(w, xw)}[y^{b - a}]G(xw/y,yw) \\ = \ & [x^aw^n]\frac{1}{1 - wG(w, xw)}[y^{b - a}]\frac{1}{1 - yw(1 + C(xw^2))} \\ = \ & [x^aw^n]\frac{1}{1 - wG(w, xw)}[y^{b - a}]\sum_{k\ge 0} \left(w\left(1 + C(xw^2)\right)\right)^i y^i \\ = \ & [x^aw^n]\frac{w^{b - a}\left(1 + C(xw^2)\right)^{b - a}}{1 - wG(w, xw)} \\ = \ & [x^aw^{n + a - b}]\frac{\left(1 + C(xw^2)\right)^{b - a}}{1 - w(1 - xw(1 + C(xw^2)))^{-1}} \\ = \ & [x^aw^{n + a - b}]\frac{\left(1 + C(xw^2)\right)^{b - a}(1 - xw(1 + C(xw^2)))}{1 - xw(1 + C(xw^2)) - w} \end{aligned}\]

到这一步,考虑对 \(\forall k\),有

\[[x^a] x^kw^k(1 + C(xw^2))^{b - a + k} = w^{2a - k}[x^a]x^k(1 + C(x))^{b - a + k} \]

我们知道,无论我们要的系数是什么,\((1 + C(xw^2))^{b - a + k}\) 都是一个多项式,因此我们想提取的 \(x^{a - k}\) 项一定形如 \(f_k(xw^2)^{a - k}\)。所以我们只需要提取出 \(w^{2(a - k)}\),之后对提取 \(x^{a - k}\) 项系数来说这多项式就等价于 \((1 + C(x))^{b - a + k}\) 了。这自然能证明如上的公式。

上面的结论想用到分式上还得讨论一下。上下两个部分得到的都是一个 \(xwf(xw^2)\) 的形式,我们需要提取 \(x^{a}\) 项的 \(w\),也就需要总共提取一个 \(w^{2a}\),并在 \(x\) 前留下一个 \(w^{-2}\)。具体看如下的推导:

\[\begin{aligned} = \ & [x^aw^{n + a - b}]\frac{\left(1 + C(xw^2)\right)^{b - a}(1 - xw(1 + C(xw^2)))}{1 - xw(1 + C(xw^2)) - w} \\ = \ & [w^{n + a - b}] w^{2a}[x^a] \frac{\left(1 + C(x)\right)^{b - a}(1 - xw^{1-2}(1 + C(x)))}{1 - xw^{1-2}(1 + C(x)) - w} \\ = \ & [x^aw^{n - a - b}]\frac{\left(1 + C(x)\right)^{b - a}(1 - x(1 + C(x))) / w}{1 - x(1 + C(x))/w - w} \end{aligned}\]

不妨设置哑元 \(t\),令 \(t^k = [x^a]x^k (1 + C(x))^{b - a + k}\),显然有 \(t^0 = [x^a] (1 + C(x))^{b - a} = 1\)。有

\[\begin{aligned} = \ & [x^aw^{n - a - b}]\frac{\left(1 + C(x)\right)^{b - a}(1 - x(1 + C(x))) / w}{1 - x(1 + C(x))/w - w} \\ = \ & [w^{n - a - b}]\frac{1 - t/w}{1 - t/w - w} \\ = \ & [w^{n - a - b}](1 - t/w) \sum_{i\ge 0} (t/ w + w)^i \\ = \ & [w^{n - a - b}](1 - t/w) \sum_{i\ge 0} \sum_{k\ge 0} \binom{i + k}{i} t^i w^{k - i} \\ = \ & \sum_{i\ge 0} \binom{n - a - b + 2i}{i} t^i - \sum_{i\ge 0} \binom{n - a - b + 1 + 2i}{i} t^{i + 1} \end{aligned}\]

最后的问题是如何算 \(t^k\),以及顺便验证 \(t^0 = 1\)
考虑 \(t\) 是一个存在复合逆的函数的复合方程的形式,直觉告诉我们可以通过另类拉格朗日反演得到简洁的形式。

\[\begin{aligned} t^k &= [x^a]x^k (1 + C(x))^{b - a + k} \\ &= [x^a] \left(\frac{x^k}{(1 + x)^2}\right)^k (1 + x)^{b - a + k} \frac{1 - x}{(1 + x)^3} (1 + x)^{2(a + 1)} \\ &= [x^{a - k}] (1 - x)(1 + x)^{a + b - k - 1} \\ &= \binom{a + b - k - 1}{a - k} -\binom{a + b - k - 1}{a - k - 1} \end{aligned}\]

到这里就完全解决了本题。注意 \(t^{a + b} = [b = 0]\)

总时间复杂度 \(O(n)\)

Submission.

posted @ 2023-03-17 16:14  joke3579  阅读(117)  评论(2编辑  收藏  举报