2025XYD Summer Camp 7.11 模考
T1
TTT 组询问,每组询问给定 n,mn,mn,m,求
(nm)−1⋅∑i=1n∑j=1mlcm(i,j) (nm)^{-1}\cdot\sum_{i=1}^n\sum_{j=1}^m\operatorname{lcm}(i,j) (nm)−1⋅i=1∑nj=1∑mlcm(i,j)
对 109+710^9+7109+7 取模。T≤20000T\le 20000T≤20000,n,m≤107n,m\le 10^7n,m≤107。
把逆元放一边,先把式子化简:
∑i=1n∑j=1mlcm(i,j)=∑i=1n∑j=1mijgcd(i,j)=∑d=1min(n,m)d∑i=1⌊n/d⌋∑j=1⌊m/d⌋ij[gcd(i,j)=1]=∑d=1min(n,m)d∑k=1min(⌊n/d⌋,⌊m/d⌋)k2μ(k)∑i=1⌊n/kd⌋∑j=1⌊m/kd⌋ij
\begin{aligned}\sum_{i=1}^n\sum_{j=1}^m\operatorname{lcm}(i,j)&=\sum_{i=1}^n\sum_{j=1}^m\frac{ij}{\gcd(i,j)}\\&=\sum_{d=1}^{\min(n,m)}d\sum_{i=1}^{\lfloor n/d\rfloor}\sum_{j=1}^{\lfloor m/d\rfloor}ij[\gcd(i,j)=1]\\&=\sum_{d=1}^{\min(n,m)}d\sum_{k=1}^{\min(\lfloor n/d\rfloor,\lfloor m/d\rfloor)}k^2\mu(k)\sum_{i=1}^{\lfloor n/kd\rfloor}\sum_{j=1}^{\lfloor m/kd\rfloor}ij\end{aligned}
i=1∑nj=1∑mlcm(i,j)=i=1∑nj=1∑mgcd(i,j)ij=d=1∑min(n,m)di=1∑⌊n/d⌋j=1∑⌊m/d⌋ij[gcd(i,j)=1]=d=1∑min(n,m)dk=1∑min(⌊n/d⌋,⌊m/d⌋)k2μ(k)i=1∑⌊n/kd⌋j=1∑⌊m/kd⌋ij
令 sum(n)=∑i=1n=n(n+1)2\displaystyle \operatorname{sum}(n)=\sum_{i=1}^n=\frac{n(n+1)}{2}sum(n)=i=1∑n=2n(n+1),
∑d=1min(n,m)d∑k=1min(⌊n/d⌋,⌊m/d⌋)k2μ(k)∑i=1⌊n/kd⌋∑j=1⌊m/kd⌋ij=∑d=1min(n,m)d∑k=1min(⌊n/d⌋,⌊m/d⌋)k2μ(k)sum(⌊n/kd⌋)sum(⌊m/kd⌋)=∑Tsum(⌊n/T⌋)sum(⌊m/T⌋)T2∑d∣Td−1μ(T/d)
\begin{aligned}\sum_{d=1}^{\min(n,m)}d\sum_{k=1}^{\min(\lfloor n/d\rfloor,\lfloor m/d\rfloor)}k^2\mu(k)\sum_{i=1}^{\lfloor n/kd\rfloor}\sum_{j=1}^{\lfloor m/kd\rfloor}ij&=\sum_{d=1}^{\min(n,m)}d\sum_{k=1}^{\min(\lfloor n/d\rfloor,\lfloor m/d\rfloor)}k^2\mu(k)\operatorname{sum}(\lfloor n/kd\rfloor)\operatorname{sum}(\lfloor m/kd\rfloor)\\&=\sum_{T}\operatorname{sum}(\lfloor n/T\rfloor)\operatorname{sum}(\lfloor m/T\rfloor)T^2\sum_{d|T}d^{-1}\mu(T/d)\end{aligned}
d=1∑min(n,m)dk=1∑min(⌊n/d⌋,⌊m/d⌋)k2μ(k)i=1∑⌊n/kd⌋j=1∑⌊m/kd⌋ij=d=1∑min(n,m)dk=1∑min(⌊n/d⌋,⌊m/d⌋)k2μ(k)sum(⌊n/kd⌋)sum(⌊m/kd⌋)=T∑sum(⌊n/T⌋)sum(⌊m/T⌋)T2d∣T∑d−1μ(T/d)
考虑 f(T)=∑d∣Td−1μ(T/d)f(T)=\sum_{d|T}d^{-1}\mu(T/d)f(T)=∑d∣Td−1μ(T/d),可以 O(nlogn)O(n\log n)O(nlogn) 暴力算卷积,也可以推推式子用线性筛做。最后整除分块以下即可。时间复杂度 O(nlogn+Tn)O(n\log n+T\sqrt n)O(nlogn+Tn) 或 O(n+Tn)O(n+T\sqrt n)O(n+Tn)。
T2
给定一个长度为 nnn 的数列 a0a_0a0 和 mmm 个二元组,另有 mmm 个数列 a1,⋯ ,ama_1,\cdots,a_ma1,⋯,am,满足对于第 iii 个数列 aia_iai 和第 iii 个二元组 (xi,yi)(x_i,y_i)(xi,yi)(其中 xi∈[1,n]x_i\in[1,n]xi∈[1,n]),aia_iai 只有第 xix_ixi 个元素与 a0a_0a0 不同且为 yiy_iyi,其余均与 a0a_0a0 相同。你需要选出一个 { 1,2,⋯ ,n }\set{1,2,\cdots,n}{1,2,⋯,n} 的子序列 { p1,p2,⋯ ,pk }\set{p_1,p_2,\cdots,p_k}{p1,p2,⋯,pk},满足 ∀i∈[0,m],j∈[1,k−1]\forall i\in[0,m],j\in[1,k-1]∀i∈[0,m],j∈[1,k−1],ai,pj≤ai,pj+1a_{i,p_j}\le a_{i,p_{j+1}}ai,pj≤ai,pj+1。求选出子序列长度的最大值。
1≤n,m≤1051\le n,m\le 10^51≤n,m≤105,1≤ai,yi≤1051\le a_i,y_i\le 10^51≤ai,yi≤105。
令 mxi=maxj=0mai,jmx_i=\max_{j=0}^m a_{i,j}mxi=maxj=0mai,j,mni=minj=0mai,jmn_i=\min_{j=0}^m a_{i,j}mni=minj=0mai,j,那么两个数 x,yx,yx,y(x<yx<yx<y)能同时在 ppp 里当且仅当 mxx≤a0,y,a0,x≤mnymx_x\le a_{0,y}, a_{0,x}\le mn_ymxx≤a0,y,a0,x≤mny。考虑 dp,设 f(i)f(i)f(i) 表示当前 p⊆{1,2,⋯ ,i}p\subseteq \{1,2,\cdots,i\}p⊆{1,2,⋯,i} 且 i∈pi\in pi∈p 的最长长度,那么有
f(i)=maxj<i,mxj≤a0,i,a0,j≤mnif(j)+1
f(i)=\max_{j<i,mx_j\le a_{0,i},a_{0,j}\le mn_i} f(j)+1
f(i)=j<i,mxj≤a0,i,a0,j≤mnimaxf(j)+1
可以发现这相当于动态往一个平面直角坐标系的第一象限里加点,点有点权,并且询问一个左下角矩形中点的权值的最大值。直接大力树套树(树状数组套 fhq)即可,当然用 cdq 分治更方便。
#include <bits/stdc++.h>
bool MemoryST; using namespace std;
#define ll long long
#define mk make_pair
#define open(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define lowbit(x) ((x) & (-(x)))
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define BCNT __builtin_popcount
#define cost_time (1e3 * clock() / CLOCKS_PER_SEC) << "ms"
#define cost_space (abs(&MemoryST - &MemoryED) / 1024.0 / 1024.0) << "MB"
const int inf = 0x3f3f3f3f;
const ll linf = 1e18;
mt19937 rnd(random_device{}());
template<typename T> void chkmax(T& x, T y) { x = max(x, y); }
template<typename T> void chkmin(T& x, T y) { x = min(x, y); }
template<typename T> T abs(T x) { return (x < 0) ? -x : x; }
const int maxn = 1e5 + 5;
int n, m, mn[maxn], mx[maxn], a[maxn];
// 线段树区间 -> mx[j]
// 平衡树区间 -> a[j]
namespace FastIn {int read() {char c = getchar(); int f = 1;for (; c < '0' || c > '9'; c = getchar())if (c == '-') f = -f;int s = 0;for (; c >= '0' && c <= '9'; c = getchar())s = (s << 1) + (s << 3) + (c ^ 48);return s * f;}
} using namespace FastIn;
namespace Structure {const int N = 5e6 + 5;struct Tree {int pos, mx, k, pri; // a[j] 子树最大 自己 优先值int ls, rs, siz;Tree() {ls = rs = siz = 0;}} T[N]; int cnt = 0;void update(int rt) {T[rt].mx = T[rt].k, T[rt].siz = 1;if (T[rt].ls) chkmax(T[rt].mx, T[T[rt].ls].mx), T[rt].siz += T[T[rt].ls].siz;if (T[rt].rs) chkmax(T[rt].mx, T[T[rt].rs].mx), T[rt].siz += T[T[rt].rs].siz;}void split(int rt, int tg, int &l, int &r) {if (rt == 0) return l = r = 0, void(0);if (T[rt].pos <= tg) {l = rt; split(T[rt].rs, tg, T[rt].rs, r);} else r = rt, split(T[rt].ls, tg, l, T[rt].ls);update(rt);}void split1(int rt, int tg, int &l, int &r) {if (rt == 0) return l = r = 0, void(0);if (T[T[rt].ls].siz + 1 <= tg) {l = rt; split1(T[rt].rs, tg - T[T[rt].ls].siz - 1, T[rt].rs, r);} else r = rt, split1(T[rt].ls, tg, l, T[rt].ls);update(rt);}int merge(int l, int r) {if (l == 0 || r == 0) return l | r;if (T[l].pri < T[r].pri) {T[l].rs = merge(T[l].rs, r), update(l);return l;} else {T[r].ls = merge(l, T[r].ls), update(r);return r;}}struct Seg {int root;Seg() { root = 0; }int ask(int K) { // 查 <= K 中最大的if (root == 0) return 0;int l, mid, r; split(root, K, l, r);if (l == 0) return 0;int ans = T[l].mx;root = merge(l, r); return ans;} void insert(int pos, int k) { // 位置在 pos,值为 kint l, mid, r; split(root, pos, l, r), split1(l, T[l].siz - 1, l, mid);if (T[mid].pos == pos) { chkmax(T[mid].k, k), T[mid].mx = T[mid].k;l = merge(l, mid), root = merge(l, r);} else {int now = ++ cnt; T[now].k = T[now].mx = k, T[now].pos = pos;T[now].pri = rnd();mid = merge(mid, now), l = merge(l, mid), root = merge(l, r);}}} seg[maxn];int query(int now, int K) { // mn[i] a[i]int res = 0;for (int i = now; i; i -= lowbit(i))chkmax(res, seg[i].ask(K));return res;} void modify(int now, int pos, int K) { // a[j] mx[j]for (int i = now; i <= 1e5; i += lowbit(i))seg[i].insert(pos, K);}
} using Structure :: query; using Structure :: modify;
namespace Subtask1 {const int N = 5005;bool chk() { return n <= 5000; }int f[N];int main() {f[1] = 1;for (int i = 2; i <= n; i ++) {f[i] = 1;for (int j = 1; j < i; j ++) if (a[j] <= mn[i] && mx[j] <= a[i])chkmax(f[i], f[j] + 1);} int ans = 0;for (int i = 1; i <= n; i ++) chkmax(ans, f[i]);printf("%d\n", ans);return 0;}
}
bool MemoryED; int main() { // open(3);n = read(), m = read();for (int i = 1; i <= n; i ++)a[i] = read(), mn[i] = mx[i] = a[i];for (int i = 1; i <= m; i ++) {int x, y; x = read(), y = read();chkmax(mx[x], y), chkmin(mn[x], y);} if (Subtask1 :: chk()) return Subtask1 :: main();modify(a[1], mx[1], 1);for (int i = 2; i <= n; i ++) {int now = query(mn[i], a[i]) + 1;// cout << now << '\n';modify(a[i], mx[i], max(1, now));} printf("%d\n", query(1e5, 1e5));return 0;
}
赛时用的线段树被卡常了(悲
T3
给定一棵 nnn 个点的树,每个点有 1/21/21/2 的概率是关键点,求连通这些关键点的最小连通块的边数的 kkk 次幂的期望,对 109+710^9+7109+7 取模。
1≤n≤1051\le n\le 10^51≤n≤105,1≤k≤2001\le k\le 2001≤k≤200。
首先把答案乘 2n2^n2n,问题变成算所有可能的连通块的边数的 kkk 次幂的和。先考虑给定一组关键点,有没有什么简单的方法快速判断每条边是否在形成的连通块里。考虑将原树钦定一个根,那么我们可以找一个点 uuu 作为连通块的根,使得所有关键点都在它的子树里;对于所有在 uuu 的子树中不是 uuu 的节点 vvv,它和它父亲的连边在连通块中当且仅当 vvv 的子树中有关键点。这样计数要求 uuu 必须确实在连通块里,否则形成的连通块就不是边数最少的。
那么我们考虑枚举每个点 uuu 作为连通块的根时对答案的贡献(此时 uuu 一定在连通块里,但是不一定是关键点,因此当 uuu 不是关键点时,有些关键点形成的连通块实际上不包含 uuu)。考虑逐个枚举 uuu 的孩子 vvv,将 vvv 作为根的答案合并进 uuu 中。先考虑怎么更新新连通块的边数的 kkk 次幂,假设枚举到 vvv,在把包含 vvv 的连通块加进包含 uuu 的连通块之前连通块中有 aaa 条边,vvv 自己有 bbb 条边;那么把 vvv 合并进来后总共有 a+b+1a+b+1a+b+1 条边(加的 111 是连接 u,vu,vu,v 的边),此时的贡献为 (a+b+1)k=∑i=0k(ki)ai(b+1)k−i(a+b+1)^k=\sum_{i=0}^k \binom kia^i(b+1)^{k-i}(a+b+1)k=∑i=0k(ik)ai(b+1)k−i,也就是我们要维护不同次幂下的边数和。
我们设 f(u,k)f(u,k)f(u,k) 表示所有以 uuu 为根的所有连通块的边数的 kkk 次幂的和(uuu 是或不是关键点都计算在内,也会包括 uuu 一个点单独作为一个连通块的情况,此时显然 uuu 必为关键点)。特别地,当 uuu 为叶子节点的时候连通块边数为 000,此时 f(u,0)=00f(u,0)=0^0f(u,0)=00;不过考虑 (x+0)k=∑i=0k(ki)0ixi−k(x+0)^k=\sum_{i=0}^k \binom ki0^ix^{i-k}(x+0)k=∑i=0k(ik)0ixi−k 仅当 000^000 取 111 的时候成立,于是我们令 f(u,0)=1f(u,0)=1f(u,0)=1(这一点非常重要)。
一样考虑枚举孩子 vvv,令 g(v,k)g(v,k)g(v,k) 表示以 uuu 为根的所有连通块的边数加一的 kkk 次幂,那么根据 (b+1)k=∑i=0k(ki)bk(b+1)^k=\sum_{i=0}^k\binom{k}{i}b^k(b+1)k=∑i=0k(ik)bk,有 g(v,k)=∑i=0k(ki)f(v,i)g(v,k)=\sum_{i=0}^k\binom kif(v,i)g(v,k)=∑i=0k(ik)f(v,i);根据 (a+b+1)k=∑i=0k(ki)ai(b+1)k−i(a+b+1)^k=\sum_{i=0}^k\binom kia^i(b+1)^{k-i}(a+b+1)k=∑i=0k(ik)ai(b+1)k−i,假设 f′f'f′ 是我们想得到的加入 vvv 后新的数组,那么 f′(u,k)=f(u,k)+g(v,k)+∑i=0k(ki)f(u,i)g(v,k−i)f'(u,k)=f(u,k)+g(v,k)+\sum_{i=0}^k\binom ki f(u,i)g(v,k-i)f′(u,k)=f(u,k)+g(v,k)+∑i=0k(ik)f(u,i)g(v,k−i)。
- 不妨从 uuu 合并第一个孩子 vvv 的答案开始考虑,此时对于所有 k>0k>0k>0 的 f(u,k)f(u,k)f(u,k) 均为 000 但是 f(u,0)=1f(u,0)=1f(u,0)=1,合并的式子就是 f′(u,k)=g(v,k)+g(v,k)f'(u,k)=g(v,k)+g(v,k)f′(u,k)=g(v,k)+g(v,k),这样非常巧妙处理了 uuu 是或不是关键点的情况。再看 f′(u,0)f'(u,0)f′(u,0) 的更新,可以发现 f′(u,0)=1+g(v,0)+g(v,0)f'(u,0)=1+g(v,0)+g(v,0)f′(u,0)=1+g(v,0)+g(v,0),也即 uuu 单独一个点作为连通块(此时必须要求 uuu 是关键点)、uuu 是或不是关键点连上 vvv 的连通块作为一个大连通块。实际上当 uuu 不是关键点时连上 vvv 的连通块是不合法,但是为了后面统计贡献我们不立即删除。
- 接下来的每次合并,f′(u,k)=f(u,k)+g(v,k)+∑i=0k(ki)f(u,i)g(v,k−i)f'(u,k)=f(u,k)+g(v,k)+\sum_{i=0}^k\binom{k}{i}f(u,i)g(v,k-i)f′(u,k)=f(u,k)+g(v,k)+∑i=0k(ik)f(u,i)g(v,k−i) 中,f(u,k)f(u,k)f(u,k) 相当于之前的贡献,g(v,k)g(v,k)g(v,k) 相当于 uuu 不是关键点时 vvv 和 uuu 合并成连通块的贡献,后面二项式定理合并的部分相当于 vvv 的连通块和 uuu 之前的连通块进行组合(uuu 单独作为连通块也算在内,此时就很好处理了 uuu 是或不是关键点与 vvv 合并的贡献)。
做完这些合并之后我们考虑删除之前 uuu 是非关键点时错误连通块的贡献,总共需要删掉所有孩子 vvv 的 g(v,k)g(v,k)g(v,k)。注意是把整棵树都做完一遍 dp 后再删贡献,因为 uuu 作为非关键点连单独一个子树作为连通块,再连到其祖先上也是一种合法情况。
这么进行树形 dp 的时间复杂度是 O(nk2)O(nk^2)O(nk2) 的,不足以通过本题。
考虑将通常幂转下降幂,即维护一个新的 F(v,k)F(v,k)F(v,k) 表示所有以 uuu 为根的所有连通块的边数的 kkk 次下降幂的和,G(v,k)G(v,k)G(v,k) 的定义同理。先考虑把 F(v,k)F(v,k)F(v,k) 变成 G(v,k)G(v,k)G(v,k),考虑二项式定理的下降幂版本 (x+y)k‾=∑i=0k(ki)xi‾yk−i‾(x+y)^{\underline k}=\sum_{i=0}^k\binom{k}{i}x^{\underline{i}}y^{\underline{k-i}}(x+y)k=∑i=0k(ik)xiyk−i,带入 x=b,y=1x=b,y=1x=b,y=1 有 (b+1)k‾=∑i=0k(ki)1i‾bk−i‾(b+1)^{\underline{k}}=\sum_{i=0}^k\binom ki1^{\underline i}b^{\underline{k-i}}(b+1)k=∑i=0k(ik)1ibk−i,我们发现 111 的下降幂下降两次变成 000 了没了!于是有 (b+1)k‾=bk‾+[k>0]kbk−1‾(b+1)^{\underline k}=b^{\underline k}+[k>0]kb^{\underline {k-1}}(b+1)k=bk+[k>0]kbk−1,所以 G(v,k)=F(v,k)+[k>0]kF(v,k−1)G(v,k)=F(v,k)+[k>0]kF(v,k-1)G(v,k)=F(v,k)+[k>0]kF(v,k−1)。
再考虑合并 G(v,k)G(v,k)G(v,k),接着带入 (a+b+1)k‾=∑i=0k(ki)ai‾(b+1)k−i‾(a+b+1)^{\underline k}=\sum_{i=0}^k\binom kia^{\underline i}(b+1)^{\underline{k-i}}(a+b+1)k=∑i=0k(ik)ai(b+1)k−i 得到 F′(u,k)=F(u,k)+G(v,k)+∑i=0k(ki)F(u,k−i)G(v,i)F'(u,k)=F(u,k)+G(v,k)+\sum_{i=0}^k\binom kiF(u,k-i)G(v,i)F′(u,k)=F(u,k)+G(v,k)+∑i=0k(ik)F(u,k−i)G(v,i) 基本上不变,那哪里能优化呢?
所以就做完了。核心是考虑 aa‾=0a^{\underline a}=0aa=0。
第二类斯特林数递推:考虑 {nk}={n−1k−1}+k{n−1k}\left\{\begin{matrix}n\\k\end{matrix}\right\}=\left\{\begin{matrix}n-1\\k-1\end{matrix}\right\}+k\left\{\begin{matrix}n-1\\k\end{matrix}\right\}{nk}={n−1k−1}+k{n−1k},边界即为 {n0}=0\left\{\begin{matrix}n\\0\end{matrix}\right\}=0{n0}=0。
int C[maxk][maxk], S[maxk][maxk], siz[maxn];
void init() {C[0][0] = S[0][0] = 1;for (int i = 1; i <= k; i ++) {C[i][0] = C[i][i] = 1, S[i][0] = 0, S[i][i] = 1;for (int j = 1; j < i; j ++) {C[i][j] = add(C[i - 1][j], C[i - 1][j - 1]),S[i][j] = add(mul(S[i - 1][j], j), S[i - 1][j - 1]);// cout << C[i][j] << " \n"[j == i - 1];}}
}
int tmp[maxk];
void dfs(int u, int fa) {F[u][0] = 1, siz[u] = 1;for (int i = head[u], v; i; i = e[i].nxt) {if ((v = e[i].to) == fa) continue;dfs(v, u);for (int k = 0; k <= :: k && k <= siz[v]; k ++) G[v][k] = add(F[v][k], k > 0 ? mul(k, F[v][k - 1]) : 0);for (int k = 0; k <= :: k && k <= siz[u] + siz[v] - 1; k ++) {tmp[k] = add(F[u][k], G[v][k]);for (int j = max(0, k - siz[u]); j <= k && j <= siz[v]; j ++)addto(tmp[k], mul(C[k][j], mul(F[u][k - j], G[v][j])));} for (int k = 0; k <= :: k; k ++)F[u][k] = tmp[k], tmp[k] = 0;siz[u] += siz[v];}
} int ans[maxn];
void dfs1(int u, int fa) {for (int i = head[u], v; i; i = e[i].nxt) {if ((v = e[i].to) == fa) continue;dfs1(v, u);for (int k = 0; k <= :: k; k ++)subto(F[u][k], G[v][k]);} // for (int i = 0; i <= k; i ++)// cout << u << ' ' << i << ' ' << F[u][i] << '\n';
}
bool MemoryED; int main() {scanf("%d %d", &n, &k);for (int i = 1, u, v; i < n; i ++)scanf("%d %d", &u, &v), addEdge(u, v), addEdge(v, u);init(), dfs(1, 0), dfs1(1, 0); int all = 0;for (int i = 1; i <= n; i ++) {for (int k = 0; k <= :: k; k ++)addto(ans[i], mul(S[:: k][k], F[i][k]));// cout << i << ' ' << ans[i] << '\n';addto(all, ans[i]);} printf("%d\n", divv(all, qp(2, n)));return 0;
}