2025.3.8 NOI 模拟赛 题解
T1 集合(set)牛客276703 Rookhopper's Tour
题意
在 \(n\times m\) 的矩形上选择 \(k\) 个位置,满足同一行、同一列至多只有一个,且对于任意被选择的位置,一定可以找到另一个被选择的位置满足两者 \(x\) 坐标之差为 \(0\) 或 \(y\) 坐标差为 \(0\),求方案数,\(n,m\le5\times10^6\),\(q\) 组 \(k\le\min(n,m)\),\(q\le5\)
分析
考虑容斥,枚举 \(0\le i\le k\) 表示钦定 \(i\) 个点不满足存在另一个点与之坐标差为 \(1\)
则答案为 \(\sum_{i=0}^k(-1)^i i!(k-i)! F(n,i,k) F(m,i,k)\),其中 \(F(n,i,k)\) 表示 在 \(1\sim n\) 中选择 \(i\) 个关键数,然后在非关键数中选择 \(k-i\) 个,满足对于任意关键数 \(x\),\(x+1,x-1\) 都没有被选择 的方案数
假设选择的关键数为 \(p_{1\sim i}\)
令 \(x_i=p_i-p_{i-1}\)(假定 \(p_0=0\))
则显然有
- \(x_1\ge 1\)
- \(\forall 1<j\le i,x_j\ge 2\)
- \(\sum_j x_j\le n\)
- 此时非关键数中可以被选择的(以下称为 空位)有 \(\max(0,x_1-2)+\sum_{j=2}^i\max(0,x_j-3)+\max(0,n-1-\sum_j x_j)\) 个
令 \(x_1\gets x_1+1\),并令 \(x_{i+1}=n+3-\sum_{1\le j\le i}x_j\)
则上述要求变为
- \(\forall 1\le j\le i+1,x_j\ge 2\)
- \(\sum_j x_j=n+3\)
- 空位有 \(\sum_{j=1}^{i+1}\max(0,x_j-3)=\sum_{j=1}^{i+1}(x_j-3+[x_j=2])=n-3i+\sum_{j=1}^{i+1}[x_j=2]\)
枚举 \(x_j=2\) 的数量 \(p\),每个空位作为一个单元(\(n-3i+p\) 个),\(x\) 中每个极长为 \(2\) 的子段作为一个单元(\((i+1)-p-1\) 个),则选出 \(n-3i+p\) 个空位的方案数为
每种方案的贡献为
因此
于是可以 \(O(1)\) 计算一个 \(F\)
总时间复杂度 \(O(n+m+\sum k)\)
代码:
#include <bits/stdc++.h>
using namespace std;
constexpr int M = 1e9+7;
inline constexpr int add(int x, int y){x += y;return x >= M? x - M : x;}
inline constexpr int sub(int x, int y){x -= y;return x < 0? x + M : x;}
inline constexpr int mul(int a, int b){return 1ll * a * b % M;}
template <typename... Args> inline constexpr int mul(int x, int y, int z, Args... arg){return mul(mul(x, y), z, arg...);}
inline constexpr int fpw(int a, int b){int ret = 1;for (; b; b >>= 1, a = mul(a, a))if (b & 1)ret = mul(ret, a);return ret;}
int n, m, q, fc[10000010], iv[10000010], ivfc[10000010];
int C(int n, int m){return n < 0 || m < 0 || n < m? 0 : mul(fc[n], ivfc[m], ivfc[n - m]);}
int A[5000010], B[5000010];
void cal(int *A, int n, int k){//let A_i = choose i key pos in [1,n], choose k - i nkey pos, key pos not next to other key / nkey
for (int i = 0; i <= k; ++i)
A[i] = mul(i ^ k? C(n - 2 * i, k - i) : 1, C(n - k + 1, i));
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m >> q;
fc[0] = fc[1] = iv[1] = ivfc[0] = ivfc[1] = 1;
for (int i = 2; i <= 1e7; ++i)fc[i] = mul(fc[i - 1], i), ivfc[i] = mul(ivfc[i - 1], iv[i] = mul(iv[M % i], M - M / i));
while (q--){
int k, res = 0;cin >> k;cal(A, n, k);cal(B, m, k);
for (int i = 0; i <= k; ++i)res = (i & 1? sub : add)(res, mul(A[i], B[i], fc[i], fc[k - i]));
cout << res << ' ';
}
return 0;
}
/*
3 4 3
1 2 3
0 30 24
*/
//https://ac.nowcoder.com/acm/contest/view-submission?submissionId=76134373
T2 树(tree)CF1111E Tree
题意
给定一棵 \(n\) 个点的树,\(q\) 次询问,每次给定 \(m,r\) 和 \(k\) 个关键点,将这 \(k\) 个点划分为不超过 \(m\) 个集合,使得令 \(r\) 为根时同一集合中两点之间没有祖先关系,求方案数,\(\sum k\le n\le10^5,m\le\min(k,300)\)
分析
常规思路是建立虚树后树形 \(dp\),令 \(f_{u,i}\) 表示子树 \(u\) 中划分为 \(i\) 个集合的合法方案数,但是这样似乎单次询问为 \(O(km^2)\),总计 \(O(qm^2)\),且难以继续优化
转换思路,若将 \(k\) 个点按 \(dfn\) 排序,则对于任意一个点 \(x\),所有不能和它在同一集合中的点(即其祖先)都在它前面
设 \(x\) 到 \(r\) 的路径上关键点数量为 \(f(x)\)(不含 \(x\)),容易树剖 \(O(k\log^2)\) 求出,也可全局平衡二叉树 \(O(k\log n)\) 求出
将所有关键点按 \(f\) 排序后,令 \(dp_{i,j}\) 表示前 \(i\) 个点划分为 \(j\) 个集合的方案,则 \(dp_{1,1}=1\),答案为 \(\sum_j dp_{k,j}\),转移时类似第二类斯特林数,分为两种情况,新建一个集合则 \(dp_{i-1,j-1}\to dp_{i,j}\),若要放入之前的一个集合,前面 \(j\) 个中有 \(f_i\) 个与 \(i\) 矛盾,因此 \(\max(0,j-f_i)dp_{i-1,j}\to dp_{i,j}\)(当 \(j<f_i\) 时无法进行这种转移)
注意此时不能按 \(dfn\) 排序,因为已经换根了(实际上可以用换根后的 \(dfn\),但求起来相当麻烦,况且只需要保证每个点到根的链上所有点都在它之前,\(f\) 就满足要求)
总时间复杂度 \(O(q\log^2 n+nm)\)
T3 图(graph)
题意
定义一张图是合法的当且仅当对其顶点标号重排序后得到的图一定与原图不同,定义一张图的表示为 \(m\;u_1\;v_1\;u_2\;v_2\;\cdots\; u_m\;v_m\),其中 \(E=\{(u_i,v_i)\}\) 为图的边集,给定 \(n\),求出所有合法的 \(n\) 点的图的表示中字典序最小的一个,\(n\le5\times10^5\)
分析
比赛结果
\(48+70+12\),\(\operatorname{rk}8\)

浙公网安备 33010602011771号