July Summary

07.07

考试(DP 大赛)。

T1 是个 DP?T2 也是个 DP?T3 也是个 DP?T4 也是个 DP?

太可怕了,刚好不擅长 DP。

A subnp

\(dp_i\) 表示以含质因数 \(i\) 的数结尾满足条件的序列最长为多少。

如果一个数为 \(x\),对于所有它的质因数 \(p_i\) 找到最大 \(dp\) 值,让所有 \(dp_{p_i}\) 变为这个 \(dp\)\(+ 1\)

B div

第一问还是很简单的。

\(dp_i\) 为大小为 \(i\) 的堆最多会有几次分到质数,\(dp_i = max\{dp_{i-j} + dp_j + !isnp_i\}(1 \le j < i)\)\(isnp_i\)\(0\) 表示 \(i\) 为质数。

对于第二问,维护一个数组 \(f_i\) 表示是否在最大方案中有过 \(i\)。很好维护,不讲了。

欸这个时间复杂度好像过不了啊?\(n = 30000\) 怎么办?

但是我过了???神秘。

C exercise

\(dp_{i,j,1}\) 表示下一次向大找,反之向小找。前缀和优化一下,然后就随便写了,注意 long long 和取模。

D corn

恐怖 DP,不会。

07.08

又是 DP???

稍微熟练一下 DP,?好像什么都有,差不多都练了一下。

题很可爱,没写完。

07.09

又 是 D P ? ? ?

数位 DP,难的很难,简单的简单得离谱。

数位 DP 不就是高级的 DFS 吗???不知道怎么有蓝的。

07.10

思 维 训 练。

A P3619 魔法 - 洛谷

简单贪心(?)但还是没有场切。

如果想要完成任务 \(j\) 后不能完成 \(i\),完成任务 \(i\) 后还能完成 \(j\),那么 \(t_i+b_i>t_j+b_j\)

排个序然后挨个判断就行了。

B Decoding Genome

DP + 矩阵加速。

很显然转移为配对的矩阵,转移次数为 \(n-1\) 然后 随 便 写。

C Jee, You See?

数位 DP。我 真 的 不 想 写 D P 了 。

实际上根本不会写。

D GCD Subtraction

发现暴力模拟会炸,注意到 \(\gcd(a,b) = 1\) 时耗费大量时间,考虑合并。

\(c\) 会减几次 \(1\)。则 \(\gcd(a - c, b - c) \ne 1\)

然后乱搞一下,枚举 \(a - b\) 的因数 \(d\),求出最小 \(x=a \bmod d\)

E Words on Tree

2-SAT。然后我就不会了。

07.11

博弈论。

ε=ε=ε=ε=ε=┌(; ̄◇ ̄)┘

结果发现好像非常()。(填空,\(4\) 分)。

四大博弈模型,看懂了但不会转化。

07.12

CSP-S 模拟赛,恐怖。

A [USACO09OPEN] Cow Digit Game S

博弈论。建个博弈图,直接硬跑,查询时直接由跑出来的数组判断。

B [ZJOI2010] 数字计数

出简单数位 DP 的出题人才是好出题人。

非常简单,直接数位 DP,没什么好说的,好像还可以打表。

C P11774 [COTS 2013] 矩形覆盖 / BAKTERIJE - 洛谷

神秘。不会扫描线被秒了。

D Museums Tour

Tarjan 缩点 + DFS,注意处理缩点后的点权(在这里调了 60 min)。

场上写 BFS,下标没取模痛失 \(45 pts\)

07.14

树的直径和重心。

树的重心的性质:
  • 性质一:树的重心如果不唯一,则至多有两个,且这两个重心相邻。
  • 性质二:以树的重心为根时,所有子树的大小都不超过整棵树大小的一半。
  • 性质三:树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样。
  • 性质四:把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。
  • 性质五:在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
CF685B Kay and Snowflake - 洛谷(树的重心模板)

当时__没有写(为 07.24 思维训练__埋下伏笔)。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int n, m, f[300005], sz[300005], ans[300005];
vector<int> vc[300005];
void dfs(int x){
	int mx = 0;
	sz[x] = 1;
	ans[x] = x;
	for(int i = 0; i < vc[x].size(); i++){
		int v = vc[x][i];
		dfs(v);
		sz[x] += sz[v];
		if(sz[mx] < sz[v]) mx = v;
	}
	if(sz[mx] * 2 > sz[x]){
		int v = ans[mx];
		while((sz[x] - sz[v]) * 2 > sz[x]) v = f[v];
		ans[x] = v;
	}
}
int main(){ 
	ios::sync_with_stdio(0);
	cin.tie(0); 
	cin >> n >> m;
	for(int i = 2; i <= n; i++){
		int v;
		cin >> v;
		vc[v].push_back(i);
		f[i] = v;
	}
	dfs(1);
	while(m--){
		int v;
		cin >> v;
		cout << ans[v] << '\n';
	}
	return 0;
}
树的直径的性质:
  • 性质一:树的直径可能有多条,但这些直径必然有重复的一段或一点。

  • 性质二:树上任意一点,距离它最远的节点是直径两个端点中的其中一个。

B4016 树的直径 - 洛谷

根据性质二进行两次 dfs 就行了。实在不知道我以前为什么不会。

#include <bits/stdc++.h>
using namespace std;
int n, mx, dis[100005];
vector<int> vc[100005];
void dfs(int x, int fa){
    if(fa) dis[x] = dis[fa] + 1;
    for(int i = 0; i < vc[x].size(); i++){
        int v = vc[x][i];
        if(v == fa) continue;
        dfs(v, x);
        if(dis[v] > dis[mx]) mx = v;
    }
}
int main(){
    cin >> n;
    for(int i = 1; i <= n - 1; i++){
        int u, v;
        cin >> u >> v;
        vc[u].push_back(v);
        vc[v].push_back(u);
    }
    dfs(1, 0);
    memset(dis, 0, sizeof dis);
    dfs(mx, 0);
    cout << dis[mx];
    return 0;
}

07.15

字符串基础算法(KMP、01Trie、Manacher 等)。

P4551 最长异或路径 - 洛谷(01Trie 模板)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct p{
	ll v, w;
};
vector<p> vc[2000005];
ll n, c = 1, xr[6000005], zotrie[6000005][2];
void dfs(int x, int fa){
	for(int i = 0; i < vc[x].size(); i++){
		int v = vc[x][i].v;
		ll w = vc[x][i].w;
		if(v == fa) continue;
		xr[v] = xr[x] ^ w;
		dfs(v, x);
	}
}
void insert(ll s){
	ll u = 0;
	for(int i = 30; i >= 0; i--){
		bool x = (s >> i) & 1;
		if(!zotrie[u][x]){
			c++;
			zotrie[u][x] = c;
		}
		u = zotrie[u][x];
	}
}
ll query(ll s){
	ll ans = 0, u = 0;
	for(int i = 30; i >= 0; i--){
		bool x = (s >> i) & 1;
		if(zotrie[u][!x]){
			ans += (1 << i);
			u = zotrie[u][!x];
		}
		else u = zotrie[u][x];
	}
	return ans;
}
int main(){
	cin >> n;
	for(int i = 1; i <= n - 1; i++){
		int u, v, w;
		cin >> u >> v >> w;
		vc[u].push_back({v, w});
		vc[v].push_back({u, w});
	}
	dfs(1, 0);
	for(int i = 1; i <= n; i++) insert(xr[i]);
	ll mx = 0;
	for(int i = 1; i <= n; i++) mx = max(mx, query(xr[i]));
	cout << mx;
	return 0;
}
P3375 【模板】KMP - 洛谷
#include <bits/stdc++.h>
using namespace std;
int nxt[1000005];
string s1, s2;
int main(){
	cin >> s1 >> s2;
	int n = s2.size(), m = s1.size();
	s2 = ' ' + s2;
	s1 = ' ' + s1;
	nxt[1] = 0;
	for(int i = 2, j = 0; i <= n; i++){
		while(j && s2[i] != s2[j + 1]) j = nxt[j];
		if(s2[i] == s2[j + 1]) j++;
		nxt[i] = j;
	}
	for(int i = 1, j = 0; i <= m; i++){
		while(j && s1[i] != s2[j + 1]) j = nxt[j];
		if(s1[i] == s2[j + 1]) j++;
		if(j == n){
			cout << i - j + 1 << '\n';
			j = nxt[j];
		}
	}
	for(int i = 1; i <= n; i++) cout << nxt[i] << ' ';
	return 0;
}
P3805 【模板】manacher - 洛谷
#include <bits/stdc++.h>
using namespace std;
string S;
int ans, mxl[22000005];
int main(){
	cin >> S;
	string s = "!-";
	for(int i = 0; i < S.size(); i++){
		s += S[i];
		s += "-";
	}
	s += '@';
	int n = s.size() - 1, r = 0, mid = 0;
	for(int i = 1; i <= n; i++){
		int u = max(0, mid * 2 - i);
		int len = min(mxl[u], r - i);
		if(i > r) len = 1;
		while(s[i + len] == s[i - len]) len++;
		mxl[i] = len;
		len--;
		if(i + len > r){
			r = i + len;
			mid = i;
		}
		ans = max(ans, mxl[i]);
	}
	cout << ans - 1;
	return 0;
}

07.16

线段树进阶。

一 个 都 不 会。

其实会两个 板子 。

练习题:

(板子)SP2713 GSS4 - Can you answer these queries IV - 洛谷

(板子)T168212 DYZ AK IOI 1 加强版 - 洛谷

P4198 楼房重建 - 洛谷

P5298 [PKUWC2018] Minimax - 洛谷

P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并 - 洛谷

CF1672I PermutationForces - 洛谷

07.17

查漏补缺。补了两个题。

07.18

思维训练。

A Power Strings

KMP,但是暴力可过。

B Progressions Covering

贪心地从后往前加,没有满足条件就一直 \(k\) 加到满足为止。

C Sausage Maximization

观察题目,看到异或和最大,自然想到了 01Trie。

前后缀不相交的条件其实不用管,反正一样的数异或起来是 \(0\),不会产生影响。

那么直接用一个 01Trie 维护前缀异或和最大值,然后对于每个后缀异或和找最大就行了。

D P1937 [USACO10MAR] Barn Allocation G - 洛谷

贪心 + 线段树。

按照右端点从小到大排序,一个一个填区间就行了。

E Different Arrays

不会。

07.19

补题,不记得补了几道了,好像至少有一道。

07.21

LCA 和树上差分。

LCA 没什么好说的,树上差分也挺好理解的。

P3379 【模板】最近公共祖先(LCA) - 洛谷
#include <bits/stdc++.h>
using namespace std;
int n, m, s, dep[500005], pa[500005][20];
vector<int> vc[500005];
void dfs(int x, int fa){
	pa[x][0] = fa;
	dep[x] = dep[fa] + 1;
	for(int i = 0; i < vc[x].size(); i++)
		if(vc[x][i] != fa)
			dfs(vc[x][i], x);
}
void painit(){
	for(int i = 1; i <= 19; i++)
		for(int j = 1; j <= n; j++)
			pa[j][i] = pa[pa[j][i - 1]][i - 1];
}
int fly(int x, int d){
	for(int i = 0; i <= 19; i++)
		if((d >> i) & 1)
			x = pa[x][i];
	return x;
}
int LCA(int x, int y){
	if(dep[x] > dep[y]) swap(x, y);
	y = fly(y, dep[y] - dep[x]);
	if(x == y) return x;
	for(int i = 19; i >= 0; i--){
		if(pa[x][i] != pa[y][i]){
			x = pa[x][i];
			y = pa[y][i];
		}
	}
	return pa[x][0];
}
int main(){
	ios::sync_with_stdio(0);
	cout.tie(0);
	cin.tie(0);
	cin >> n >> m >> s;
	for(int i = 1; i <= n - 1; i++){
		int x, y;
		cin >> x >> y;
		vc[x].push_back(y);
		vc[y].push_back(x);
	}
	dfs(s, 0);
	painit();
	while(m--){
		int x, y;
		cin >> x >> y;
		cout << LCA(x, y) << '\n';
	}
	return 0;
}
P3128 [USACO15DEC] Max Flow P(树上差分)
#include <bits/stdc++.h>
using namespace std;
int n, k, maxn, nw, s[50005], sz[50005], id[50005], dep[50005], ans[50005], pa[50005][16];
vector<int> vc[50005];
void dfs(int x, int fa){
	nw++;
	sz[x] = 1;
	id[x] = nw;
	dep[x] = dep[fa] + 1;
	pa[x][0] = fa;
	for(int i = 0; i < vc[x].size(); i++){
		if(vc[x][i] != fa){
			dfs(vc[x][i], x);
			sz[x] += sz[vc[x][i]];
		}
	}
}
void painit(){
	for(int i = 1; i <= 15; i++)
		for(int j = 1; j <= n; j++)
			pa[j][i] = pa[pa[j][i - 1]][i - 1];
}
int fly(int x, int d){
	for(int i = 15; i >= 0; i--){
		if((d >> i) & 1) x = pa[x][i];
	}
	return x;
}
int LCA(int x, int y){
	if(dep[x] > dep[y]) swap(x, y);
	y = fly(y, dep[y] - dep[x]);
	if(x == y) return x;
	for(int i = 15; i >= 0; i--){
		if(pa[x][i] != pa[y][i]){
			x = pa[x][i];
			y = pa[y][i];
		}
	}
	return pa[x][0];
}
int dfans(int x, int fa){
	int sum = s[x];
	for(int i = 0; i < vc[x].size(); i++){
		if(vc[x][i] != fa){
			sum += dfans(vc[x][i], x);
		}
	}
	maxn = max(sum, maxn);
	return sum;
}
int main(){
	cin >> n >> k;
	for(int i = 1; i <= n - 1; i++){
		int x, y;
		cin >> x >> y;
		vc[x].push_back(y);
		vc[y].push_back(x);
	}
	dfs(1, 0);
	painit();
	while(k--){
		int x, y;
		cin >> x >> y;
		int lca = LCA(x, y);
		s[x]++;
		s[y]++;
		s[lca]--;
		s[pa[lca][0]]--;
	}
	dfans(1, 0);
	cout << maxn;
	return 0;
}

练习题:

P3398 仓鼠找 sugar

A and B and Lecture Rooms

P1967 [NOIP 2013 提高组] 货车运输

P3258 [JLOI2014] 松鼠的新家

P4281 [AHOI2008] 紧急集合 / 聚会

P5836 [USACO19DEC] Milk Visits S

P3038 [USACO11DEC] Grass Planting G

都比较简单。

07.22

树剖。

怎么所有人都做过练习题啊,怎么只有我没学过啊。

写了 \(4\) 题,拼尽全力无法战胜。

P3384 【模板】重链剖分/树链剖分

重链剖分的性质(部分):

  • 树上每个节点都属于且仅属于一条重链

  • 所有的重链将整棵树 完全剖分

  • 在剖分时 重边优先遍历,最后树的 DFS 序上,重链内的 DFS 序是连续的。按 DFN 排序后的序列即为剖分后的链。

相当于把树分为一堆重链,然后利用重链的性质维护问题。

// sz[i]:以 i 为根的子树大小  hs[i]: i 的重儿子  fa[i]: i 的父亲节点  tp[i]: i 所在重链的链顶节点
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int n, m, r, p, cnt, sz[100005], hs[100005], fa[100005], tp[100005], id[100005], dep[100005];
vector<int> vc[100005];
ll a[100005], tr[400005], lazy[400005], A[100005];
void down(int x, ll k, int len){
	tr[x] += k * len;
    tr[x] %= p;
	lazy[x] += k;
	lazy[x] %= p;
}
void pushdown(int x, int l, int r){
	int mid = (l + r) / 2;
	down(x * 2, lazy[x], mid - l + 1);
	down(x * 2 + 1, lazy[x], r - mid);
	lazy[x] = 0;
}
void add(int a, int x, int l, int r, int L, int R){
	if(r < L || l > R) return;
	if(L <= l && r <= R){
		tr[a] += x * (r - l + 1) % p;
        tr[a] %= p;
		lazy[a] += x;
        lazy[a] %= p;
		return;
	}
	pushdown(a, l, r);
	int mid = (l + r) / 2;
	add(a * 2, x, l, mid, L, R);
	add(a * 2 + 1, x, mid + 1, r, L, R);
	tr[a] = (tr[a * 2] + tr[a * 2 + 1]) % p;
}
ll query(int a, int l, int r, int L, int R){
	if(r < L || l > R) return 0;
	if(L <= l && r <= R){
		return tr[a] % p;
	}
	pushdown(a, l, r);
	int mid = (l + r) / 2;
	ll s = 0;
	s += query(a * 2, l, mid, L, R);
	s += query(a * 2 + 1, mid + 1, r, L, R);
	return s % p;
}
void build(int x, int l, int r){
	if(l == r){
		tr[x] = A[l];
		return;
	}
	int mid = (l + r) / 2;
	build(x * 2, l, mid);
	build(x * 2 + 1, mid + 1, r);
	tr[x] = (tr[x * 2] + tr[x * 2 + 1]) % p;
}
void dfs1(int x, int f){
    fa[x] = f;
    sz[x] = 1;
    dep[x] = dep[f] + 1;
    for(int i = 0; i < vc[x].size(); i++){
        int v = vc[x][i];
        if(v == f) continue;
        dfs1(v, x);
        sz[x] += sz[v];
        if(sz[v] > sz[hs[x]]) hs[x] = v;
    }
}
void dfs2(int x, int t){
    tp[x] = t;
    id[x] = ++cnt;
    A[cnt] = a[x];
    if(!hs[x]) return;
    dfs2(hs[x], t);
    for(int i = 0; i < vc[x].size(); i++){
        int v = vc[x][i];
        if(v == hs[x] || v == fa[x]) continue;
        dfs2(v, v);
    }
}
void addp(int x, int y, int z){
    while(tp[x] != tp[y]){
        if(dep[tp[x]] < dep[tp[y]]) swap(x, y);
        add(1, z, 1, n, id[tp[x]], id[x]);
        x = fa[tp[x]];
    }
    if(dep[x] > dep[y]) swap(x, y);
    add(1, z, 1, n, id[x], id[y]);
}
ll qryp(int x, int y){
    ll s = 0;
    while(tp[x] != tp[y]){
        if(dep[tp[x]] < dep[tp[y]]) swap(x, y);
        s += query(1, 1, n, id[tp[x]], id[x]);
        s %= p;
        x = fa[tp[x]];
    }
    if(dep[x] > dep[y]) swap(x, y);
    s = (s + query(1, 1, n, id[x], id[y])) % p;
    return s;
}
int main(){
    cin >> n >> m >> r >> p;
    for(int i = 1; i <= n; i++) cin >> a[i]; 
    for(int i = 1; i <= n - 1; i++){ 
        int x, y; 
        cin >> x >> y;
        vc[x].push_back(y);
        vc[y].push_back(x);
    }
    dfs1(r, 0);
    dfs2(r, r);
    build(1, 1, n);
    while(m--){
        int op, x;
        cin >> op >> x;
        if(op == 1){
            int y, z;
            cin >> y >> z;
            z %= p;
            addp(x, y, z);
        }
        else if(op == 2){
            int y;
            cin >> y;
            cout << qryp(x, y) <<'\n';
        }
        else if(op == 3){
            int z;
            cin >> z;
            z %= p;
            add(1, z, 1, n, id[x], id[x] + sz[x] - 1);
        }
        else{
            cout << query(1, 1, n, id[x], id[x] + sz[x] - 1) << '\n';
        }
    }
    return 0;
}

[!NOTE]

  • 要先调用 dfs1 和 dfs2,再调用 build 函数初始化线段树,不然会错

  • 用新的数组保存映射后的原数组,不要直接覆盖原数组

P2486 [SDOI2011] 染色

P4315 月下“毛景树” - 洛谷

P2146 [NOI2015] 软件包管理器 - 洛谷

07.23

补题,下午回家了。

轻重边没调出来。

07.24

思 维 训 练 。

A Minimum spanning tree for each edge

求出最小生成树并记下树边。对于每条边,作如下判断:

  • 如果是最小生成树的树边,答案即为原本的最小边权和。
  • 如果不是,在原本的最小生成树上加上此边会形成环,删去环上除了此边最大的边即为答案。倍增,稍微有点麻烦。

B A Wide, Wide Graph

很显然,连通块个数为离直径一端最远距离 \(> k\) 的点 \(+1\)

直接求出数的直径的两个端点,在此过程中可以顺便求出每个点与直径端点的距离。

然后就随便做了。

C White Lines

形似二维前缀和。一路 \(O(n^2)\) 枚举左上顶点推过去即可。

实现非常麻烦,所以没有实现。

D Kay and Snowflake

原题,刚好没写。(是的就是 CF685B Kay and Snowflake - 洛谷(树的重心模板),伏笔回收)。

肯定是要预处理什么的。

  • 树的重心的性质之一:将两颗树合并,新的重心在原来两个重心的简单路径上。

一个以 \(x\) 为根的树的重心可以由它的子树合并得来。很显然的是,以 \(x\) 为根的树的重心不可能比它的子树的重心还要低,所以在合并的时候直接从它子树的重心往上跳,满足就记录答案。

叶子节点的重心肯定是它自己。然后就可以做了。

\(O(n)\) 预处理,\(O(1)\) 查询。

E Kuro and GCD and XOR and SUM

01Trie。

听说是 \(10^5\) 个 01Trie???恐怖如斯。

居然不会炸。

07.25

组合计数类问题选讲。

递推求逆元 & \(O(1)\) 求组合数
ll jc[305], inv[305], dp[305][305];
int fpow(ll a, int b){
	ll mul = 1;
	while(b){
		if(b & 1) mul = a * mul % MOD;
		a = a * a % MOD;
		b >>= 1;
	}
	return mul;
}
void init(){
	jc[0] = 1;
	for(int i = 1; i <= n; i++)	jc[i] = jc[i - 1] * i % MOD;
	inv[n] = fpow(jc[n], MOD - 2);
	for(int i = n; i >= 1; i--) inv[i - 1] = inv[i] * i % MOD;
}
ll C(int x, int y){
	return jc[x] * inv[y] % MOD * inv[x - y] % MOD;
}
递推求组合数 \(O(n^2)\)
for(int i = 0; i <= 2000; i++) dp[i][0] = dp[i][i] = 1;
	for(int i = 1; i <= 2000; i++)
		for(int j = 1; j < i; j++)
			dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % MOD;
Lucas 定理

卢卡斯定理如下:

img

ll n, m, p, jc[300005], inv[300005];
ll fastpow(ll a, ll b){
	ll mul = 1;
	while(b){
		if(b & 1) mul = a * mul % p;
		a = a * a % p;
		b >>= 1;
	}
	return mul;
}
void init(){
	jc[0] = 1;
	for(ll i = 1; i <= p - 1; i++) jc[i] = jc[i - 1] * i % p;
	inv[p - 1] = fastpow(jc[p - 1], p - 2);
	for(ll i = p - 1; i >= 1; i--) inv[i - 1] = inv[i] * i % p;
}
ll C(ll x, ll y){
	return jc[x] * fastpow(jc[y], p - 2) % p * fastpow(jc[x - y], p - 2) % p;
}
ll Lucas(ll x, ll y){
	if(!y) return 1ll;
	return (Lucas(x / p, y / p) * C(x % p, y % p)) % p;
}
Catalan 数 \(O(n)\)
Cat[0] = Cat[1] = 1;
for(int i = 2; i <= n; i++) Cat[i] = Cat[i - 1] * (4 * i - 2) / (i + 1);
盒子与球问题

  • 鸽巢原理

​ 将 \(n\) 个物体划分为 \(k\) 组,那么至少存在一个分组,含有大于或等于 \(\left \lceil \dfrac{n}{k} \right \rceil\) 个物品。

  • 组合数的基本求解以及用法(非常重要)

    • 网格模型

      • 普通网格问题

      • 多组合数和

      • 卡特兰数 - 与树计数

        给定节点个数的二叉树个数为卡特兰数

        \[H_n = \begin{cases} \sum_{i=1}^{n} H_{i-1} H_{n-i} & n \geq 2, n \in \mathbf{N_{+}}\\ 1 & n = 0, 1 \end{cases} \]

        \[H_n = \dfrac{H_{n-1} (4n-2)}{n+1} \]

        \[H_n = \binom{2n}{n} - \binom{2n}{n-1} \]

    • 插板问题

    • 放球问题

      • 有符号
      • 无符号
    • 可重集组合数

    • 二项式定理

\[(a+b)^n=\sum_{i=0}^n\binom{n}{i}a^{n-i}b^i \]

  • 容斥

    • 补集
    • 其他容斥
posted @ 2025-07-25 09:25  KukCair  阅读(33)  评论(0)    收藏  举报