垃圾ACMer的暑假训练220717
33.3 Stirling数
[二项式反演] 若 f n = ∑ k = 0 n C n k g k \displaystyle f_n=\sum_{k=0}^n C_n^k g_k fn=k=0∑nCnkgk,则 g n = ∑ k = 0 n ( − 1 ) n − k C n k f k \displaystyle g_n=\sum_{k=0}^n (-1)^{n-k}C_n^k f_k gn=k=0∑n(−1)n−kCnkfk.
[证] ∑ k = 0 n ( − 1 ) n − k C n k f k = ∑ k = 0 n ( − 1 ) n − k C n k ∑ i = 0 k C n i g i = ∑ i = 0 n g i ∑ k = i n ( − 1 ) n − k C n k C k i \displaystyle \sum_{k=0}^n (-1)^{n-k}C_n^k f_k=\sum_{k=0}^n (-1)^{n-k}C_n^k \sum_{i=0}^k C_n^i g_i=\sum_{i=0}^n g_i\sum_{k=i}^n (-1)^{n-k}C_n^k C_k^i k=0∑n(−1)n−kCnkfk=k=0∑n(−1)n−kCnki=0∑kCnigi=i=0∑ngik=i∑n(−1)n−kCnkCki
= ∑ i = 0 n g i ∑ k = i n ( − 1 ) n − k ⋅ n ! k ! ( n − k ) ! ⋅ k ! i ! ( k − i ) ! = ∑ i = 0 n g i ∑ k = i n ( − 1 ) n − k ⋅ n ! i ! ( n − i ) ! C n − i n − k \displaystyle=\sum_{i=0}^n g_i\sum_{k=i}^n (-1)^{n-k}\cdot\dfrac{n!}{k!(n-k)!}\cdot\dfrac{k!}{i!(k-i)!}=\sum_{i=0}^n g_i\sum_{k=i}^n (-1)^{n-k}\cdot\dfrac{n!}{i!(n-i)!}C_{n-i}^{n-k} =i=0∑ngik=i∑n(−1)n−k⋅k!(n−k)!n!⋅i!(k−i)!k!=i=0∑ngik=i∑n(−1)n−k⋅i!(n−i)!n!Cn−in−k
= ∑ i = 0 n C n i g i ∑ k = i n C n − i n − k ( − 1 ) n − k = j = n − k ∑ i = 0 n C n i g i ∑ j = 0 n − i C n − i j ( − 1 ) j = ∑ i = 0 n C n i g i ( 1 − 1 ) n − i = g n C n n = g n \displaystyle =\sum_{i=0}^n C_n^i g_i\sum_{k=i}^n C_{n-i}^{n-k}(-1)^{n-k}\xlongequal{j=n-k}\sum_{i=0}^n C_n^i g_i\sum_{j=0}^{n-i} C_{n-i}^{j}(-1)^{j}=\sum_{i=0}^n C_n^i g_i(1-1)^{n-i}=g_nC_n^n=g_n =i=0∑nCnigik=i∑nCn−in−k(−1)n−kj=n−ki=0∑nCnigij=0∑n−iCn−ij(−1)j=i=0∑nCnigi(1−1)n−i=gnCnn=gn.
33.3.1 第二类Stirling数
[第二类Stirling数,Stirling子集数,整数的无序分拆数] 将 n n n个两两不同的元素划分为 k k k个互不区分的非空子集的方案数,记作 { n k } \begin{Bmatrix}n \\ k\end{Bmatrix} {nk}或 S ( n , k ) S(n,k) S(n,k).
[递推式] { n k } = { n − 1 k − 1 } + k { n − 1 k } \begin{Bmatrix}n \\ k\end{Bmatrix}=\begin{Bmatrix}n-1 \\ k-1\end{Bmatrix}+k\begin{Bmatrix}n-1 \\ k\end{Bmatrix} {nk}={n−1k−1}+k{n−1k},初始条件 { n 0 } = [ n = 0 ] \begin{Bmatrix}n \\ 0\end{Bmatrix}=[n=0] {n0}=[n=0].
[证] 每插入一个新元素时,有两种方案:
①将新元素单独放在一个子集,有 { n − 1 k − 1 } \begin{Bmatrix}n-1 \\ k-1\end{Bmatrix} {n−1k−1}种方案.
②将新元素放入一个现有的非空子集,有 k { n − 1 k } k\begin{Bmatrix}n-1 \\ k\end{Bmatrix} k{n−1k}种方案.
[性质] m n = ∑ i = 1 m C m i { n i } ⋅ i ! \displaystyle m^n=\sum_{i=1}^m C_m^i\begin{Bmatrix}n \\ i\end{Bmatrix}\cdot i! mn=i=1∑mCmi{ni}⋅i!.
[证] L H S \mathrm{LHS} LHS为将 n n n个两两不同的元素划分为 m m m个两两不同的子集(允许空集)的方案数.
R H S \mathrm{RHS} RHS为枚举非空子集个数 i i i求得的答案,其中 i ! = A i i i!=A_i^i i!=Aii是对 i i i个非空子集的排序.
[通项公式] { n m } = ∑ i = 0 m ( − 1 ) m − i i n i ! ( m − i ) ! \begin{Bmatrix}n \\ m\end{Bmatrix}=\displaystyle\sum_{i=0}^m \dfrac{(-1)^{m-i}i^n}{i!(m-i)!} {nm}=i=0∑mi!(m−i)!(−1)m−iin.
[证1] 因 f ( m ) = m n = ∑ i = 1 m C m i { n i } ⋅ i ! = ∑ i = 1 m C m i g ( i ) \displaystyle f(m)=m^n=\sum_{i=1}^m C_m^i\begin{Bmatrix}n \\ i\end{Bmatrix}\cdot i!=\sum_{i=1}^m C_m^ig(i) f(m)=mn=i=1∑mCmi{ni}⋅i!=i=1∑mCmig(i),
由二项式反演: g ( m ) = { n m } ⋅ m ! = ∑ i = 1 m ( − 1 ) m − i C m i i n \displaystyle g(m)=\begin{Bmatrix}n \\ m\end{Bmatrix}\cdot m!=\sum_{i=1}^m (-1)^{m-i}C_m^i i^n g(m)={nm}⋅m!=i=1∑m(−1)m−iCmiin,移项即证.
[证2] 设将 n n n个两两不同的元素划分到 k k k个两两不同的集合(允许空集)的方案数为 G n G_n Gn,将 n n n个两两不同的元素划分到 k k k个两两不同的非空集合(不允许空集)的方案数 F n F_n Fn.
显然 G n = k n , G n = ∑ j = 0 i C i j F j G_n=k^n,G_n=\displaystyle\sum_{j=0}^i C_i^j F_j Gn=kn,Gn=j=0∑iCijFj.
由二项式反演: F i = ∑ j = 0 i ( − 1 ) i − j C i j G j = ∑ j = 0 i ( − 1 ) i − j C i j j n = ∑ j = 0 i ( i − 1 ) i − j i ! j n j ! ( i − j ) ! \displaystyle F_i=\sum_{j=0}^i (-1)^{i-j} C_i^j G_j=\sum_{j=0}^i (-1)^{i-j} C_i^j j^n=\sum_{j=0}^i \dfrac{(i-1)^{i-j}i!j^n}{j!(i-j)!} Fi=j=0∑i(−1)i−jCijGj=j=0∑i(−1)i−jCijjn=j=0∑ij!(i−j)!(i−1)i−ji!jn.
因第二类Stirling数要求集合间互不区分,则 F i F_i Fi是 { n i } \begin{Bmatrix}n \\ i\end{Bmatrix} {ni}的 i ! i! i!倍,故 { n m } = F m m ! = ∑ i = 0 m ( − 1 ) m − i i n i ! ( m − i ) ! \begin{Bmatrix}n \\ m\end{Bmatrix}=\dfrac{F_m}{m!}=\displaystyle\sum_{i=0}^m \dfrac{(-1)^{m-i}i^n}{i!(m-i)!} {nm}=m!Fm=i=0∑mi!(m−i)!(−1)m−iin.
33.3.1.1 第二类Stirling数·行
题意 ( 0.5 s ) (0.5\ \mathrm{s}) (0.5 s)
给定整数 n ( 1 ≤ n ≤ 2 e 5 ) n\ \ (1\leq n\leq 2\mathrm{e}5) n (1≤n≤2e5),对所有 i ∈ [ 0 , n ] i\in[0,n] i∈[0,n],输出第二类Stirling数 { n i } \begin{Bmatrix}n \\ i\end{Bmatrix} {ni},答案对素数 167772161 = 2 25 × 5 + 1 167772161=2^{25}\times 5+1 167772161=225×5+1取模.
思路
167772161 167772161 167772161的原根为 3 3 3.
由通项公式 { n m } = ∑ i = 0 m ( − 1 ) m − i i n i ! ( m − i ) ! = ∑ i = 0 m ( − 1 ) m − i ( m − i ) ! ⋅ i n i ! \displaystyle\begin{Bmatrix}n \\ m\end{Bmatrix}=\sum_{i=0}^m \dfrac{(-1)^{m-i}i^n}{i!(m-i)!}=\sum_{i=0}^m \dfrac{(-1)^{m-i}}{(m-i)!}\cdot\dfrac{i^n}{i!} {nm}=i=0∑mi!(m−i)!(−1)m−iin=i=0∑m(m−i)!(−1)m−i⋅i!in.
设 f ( n ) = ( − 1 ) n n ! , g ( n ) = i n i ! f(n)=\dfrac{(-1)^n}{n!},g(n)=\dfrac{i^n}{i!} f(n)=n!(−1)n,g(n)=i!in,则 { n i } \begin{Bmatrix}n \\ i\end{Bmatrix} {ni}是 ( f ∗ g ) ( n ) (f*g)(n) (f∗g)(n)的第 i i i项的系数.用NTT计算卷积即可.时间复杂度 O ( n log n ) O(n\log n) O(nlogn).
对 f ( i ) f(i) f(i):① i i i为偶数时,取系数为 i ! i! i!的逆元 i f a c [ i ] ifac[i] ifac[i];② i i i为奇数时,取系数为 M O D − i f a c [ i ] MOD-ifac[i] MOD−ifac[i].
代码 (附带ll版的NTT)
const int MAXN = 6e5 + 5;
const ll MOD = 167772161;
int rev[MAXN]; // rev[x]表示二进制数x镜像后的结果
void NTT(ll* y, int lim, int op) { // op=1时为NTT,op=-1时为INTT
for (int i = 0; i < lim; i++) // 做一遍BRP,将系数位置调整为递归后的位置
if (rev[i] < i) swap(y[i], y[rev[i]]);
for (int l = 2; l <= lim; l <<= 1) { // 模拟合并过程,l为当前区间长度
ll g = qpow(3, (MOD - 1) / l, MOD); // 注意ll
for (int i = 0; i < lim; i += l) {
int cur = 1;
int k = l >> 1;
for (int j = 0; j < k; j++, cur = cur * g % MOD) {
ll tmp = y[i + j + k] * cur % MOD; // 注意ll
y[i + j + k] = (y[i + j] - tmp + MOD) % MOD; // 奇次项
y[i + j] = (y[i + j] + tmp) % MOD; // 偶次项
}
}
}
if (op == -1) { // INTT
reverse(y + 1, y + lim);
ll inv = qpow(lim, MOD - 2, MOD); // 注意ll
for (int i = 0; i < lim; i++) y[i] = y[i] * inv % MOD;
}
}
ll fac[MAXN], ifac[MAXN]; // 阶乘及其逆元
void init(int n) { // 预处理阶乘及其逆元
fac[0] = 1;
for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % MOD;
ifac[n] = qpow(fac[n], MOD - 2, MOD);
for (int i = n - 1; i >= 0; i--) ifac[i] = ifac[i + 1] * (i + 1) % MOD;
}
int n;
ll a[MAXN], b[MAXN]; // 多项式f和g的系数
int main() {
cin >> n;
init(n); // 预处理阶乘及其逆元
for (int i = 0; i <= n; i++) {
a[i] = (i & 1 ? MOD - ifac[i] : ifac[i]);
b[i] = qpow(i, n, MOD) * ifac[i] % MOD;
}
int lim = 1;
while (lim < (n << 1)) lim <<= 1;
for (int i = 0; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) + (i & 1) * (lim >> 1);
NTT(a, lim, 1), NTT(b, lim, 1);
for (int i = 0; i < lim; i++) a[i] = a[i] * b[i] % MOD;
NTT(a, lim, -1);
for (int i = 0; i <= n; i++) cout << a[i] << ' ';
}
33.3.1.2 染色
题意
有一个长度为 n n n的序列和 m m m种颜色.现对其染色.若染色后的序列种出现次数恰为 s s s的颜色种数为 k k k,则会得到 w k w_k wk的愉悦度.对所有染色方案,求可获得的愉悦度之和,答案对 1004535809 1004535809 1004535809取模.
第一行输入整数 n , m , s ( 1 ≤ n ≤ 1 e 7 , 1 ≤ m ≤ 1 e 5 , 1 ≤ s ≤ 150 ) n,m,s\ \ (1\leq n\leq 1\mathrm{e}7,1\leq m\leq 1\mathrm{e}5,1\leq s\leq 150) n,m,s (1≤n≤1e7,1≤m≤1e5,1≤s≤150).第二行输入 ( m + 1 ) (m+1) (m+1)个整数 w 0 , ⋯ , w m ( 0 ≤ w i < 1004535809 ) w_0,\cdots,w_m\ \ (0\leq w_i<1004535809) w0,⋯,wm (0≤wi<1004535809).
思路
1004535809 1004535809 1004535809的原根为 3 3 3.
每种方案中,对愉悦度有贡献的颜色数不超过 c o l o r = min { m , ⌊ n s ⌋ } color=\min\left\{m,\left\lfloor\dfrac{n}{s}\right\rfloor\right\} color=min{m,⌊sn⌋}.
设至少有 i i i种颜色恰出现 s s s次的方案数为 f i f_i fi.从 m m m种颜色中选 i i i种,乘 C m i C_m^i Cmi.将 n n n个位置分为 ( i + 1 ) (i+1) (i+1)个部分,其中有 i i i种恰出现 s s s次的元素,剩下 ( m − i ) (m-i) (m−i)种颜色共 ( n − i s ) (n-is) (n−is)个.先视为可重排列,乘 n ! ( s ! ) i ( n − i s ) ! \dfrac{n!}{(s!)^i(n-is)!} (s!)i(n−is)!n!. n n n个位置的前 i i i个部分都只有一种颜色,最后一个部分每个位置可取 ( m − i ) (m-i) (m−i)种颜色,乘 ( m − i ) n − i s (m-i)^{n-is} (m−i)n−is.故 f i = C m i ⋅ n ! ( s ! ) i ( n − i s ) ! ⋅ ( m − i ) n − i s f_i=C_m^i\cdot \dfrac{n!}{(s!)^i (n-is)!}\cdot (m-i)^{n-is} fi=Cmi⋅(s!)i(n−is)!n!⋅(m−i)n−is.
设有 i i i种颜色恰出现 s s s次的方案数为 a n s i ans_i ansi.由容斥原理: a n s i = ∑ j = i c o l o r ( − 1 ) j − i C j i f j \displaystyle ans_i=\sum_{j=i}^{color}(-1)^{j-i}C_j^i f_j ansi=j=i∑color(−1)j−iCjifj,
则 a n s i ⋅ i ! = ∑ j = i c o l o r ( − 1 ) j − i ( j − i ) ! ⋅ ( f j ⋅ j ! ) \displaystyle ans_i\cdot i!=\sum_{j=i}^{color}\dfrac{(-1)^{j-i}}{(j-i)!}\cdot (f_j\cdot j!) ansi⋅i!=j=i∑color(j−i)!(−1)j−i⋅(fj⋅j!),用NTT求即可.
代码
const int MAXN = 3e7 + 5;
const ll MOD = 1004535809;
int rev[MAXN]; // rev[x]表示二进制数x镜像后的结果
void NTT(ll* y, int lim, int op) { // op=1时为NTT,op=-1时为INTT
for (int i = 0; i < lim; i++) // 做一遍BRP,将系数位置调整为递归后的位置
if (rev[i] < i) swap(y[i], y[rev[i]]);
for (int l = 2; l <= lim; l <<= 1) { // 模拟合并过程,l为当前区间长度
ll g = qpow(3, (MOD - 1) / l, MOD);
for (int i = 0; i < lim; i += l) {
int cur = 1;
int k = l >> 1;
for (int j = 0; j < k; j++, cur = cur * g % MOD) {
ll tmp = y[i + j + k] * cur % MOD;
y[i + j + k] = (y[i + j] - tmp + MOD) % MOD; // 奇次项
y[i + j] = (y[i + j] + tmp) % MOD; // 偶次项
}
}
}
if (op == -1) { // INTT
reverse(y + 1, y + lim);
ll inv = qpow(lim, MOD - 2, MOD);
for (int i = 0; i < lim; i++) y[i] = y[i] * inv % MOD;
}
}
ll fac[MAXN], ifac[MAXN]; // 阶乘及其逆元
void init(int n) { // 预处理阶乘及其逆元
fac[0] = 1;
for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % MOD;
ifac[n] = qpow(fac[n], MOD - 2, MOD);
for (int i = n - 1; i >= 0; i--) ifac[i] = ifac[i + 1] * (i + 1) % MOD;
}
ll C(int n, int m) {
if (n < m) return 0;
if (n == m) return 1;
return fac[n] * ifac[m] % MOD * ifac[n - m] % MOD;
}
int n, m, s; // 序列长度、颜色数、出现次数
ll w[MAXN]; // 愉悦度
ll a[MAXN], b[MAXN]; // 多项式f和g的系数
int main() {
cin >> n >> m >> s;
for (int i = 0; i <= m; i++) cin >> w[i];
init(max({ n,m,s })); // 预处理阶乘及其逆元
int color = min(m, n / s);
int lim = 1;
while (lim < (color + 1 << 1)) lim <<= 1; // 注意color+1
for (int i = 0; i <= color; i++) {
ll inv = qpow(qpow(fac[s], i, MOD) * fac[n - i * s] % MOD, MOD - 2, MOD); // fi的分母
a[i] = C(m, i) * fac[n] % MOD * qpow(m - i, n - i * s, MOD) % MOD * inv % MOD * fac[i] % MOD;
b[i] = ((color - i) & 1 ? MOD - ifac[color - i] : ifac[color - i]);
}
for (int i = 0; i < lim; i++)
rev[i] = (rev[i >> 1] >> 1) | (i & 1) * (lim >> 1);
NTT(a, lim, 1), NTT(b, lim, 1);
for (int i = 0; i < lim; i++) a[i] = a[i] * b[i] % MOD;
NTT(a, lim, -1);
int ans = 0;
for (int i = 0; i <= color; i++) ans = ((ll)ans + (ll)w[i] * a[color + i] % MOD * ifac[i] % MOD) % MOD;
cout << ans;
}
Codeforces
Codeforces Round #808 (Div. 2) A. Difference Operations
原题指路:https://codeforces.com/contest/1708/problem/A
题意
给定一个长度为 n ( 2 ≤ n ≤ 100 ) n\ \ (2\leq n\leq 100) n (2≤n≤100)的序列 a a a,问能否进行若干次(可能为 0 0 0次)操作,使得 a i = 0 ( 2 ≤ i ≤ n ) a_i=0\ \ (2\leq i\leq n) ai=0 (2≤i≤n).操作:选择一个下标 i ∈ [ 2 , n ] i\in[2,n] i∈[2,n],令 a i − = a i − 1 a_i-=a_{i-1} ai−=ai−1,
有 t ( 1 ≤ t ≤ 100 ) t\ \ (1\leq t\leq 100) t (1≤t≤100)组测试数据.每组测试数据第一行输入一个整数 n ( 2 ≤ n ≤ 100 ) n\ \ (2\leq n\leq 100) n (2≤n≤100).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 1 ≤ a i ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq 1\mathrm{e}9) a1,⋯,an (1≤ai≤1e9).
对每组测试数据,若能进行若干次(可能为 0 0 0次)操作,使得 a i = 0 ( 2 ≤ i ≤ n ) a_i=0\ \ (2\leq i\leq n) ai=0 (2≤i≤n),则输出"YES";否则输出"NO".
思路I
有解的充要条件是: a 2 ∼ a n a_2\sim a_n a2∼an都能被 a 1 a_1 a1整除.
[证] (充) 显.
(必) a 2 a_2 a2是 a 1 a_1 a1的倍数,否则 a 2 a_2 a2不能变为 0 0 0. a 3 a_3 a3是 a 2 a_2 a2的倍数,否则 a 3 a_3 a3不能变为 0 0 0.而 a 2 a_2 a2是 a 1 a_1 a1的倍数,故 a 3 a_3 a3是 a 1 a_1 a1的倍数.以此类推.
思路II
考虑相反的问题:给定一个长度为 n n n的序列 b = [ b 1 , 0 , ⋯ , 0 ] b=[b_1,0,\cdots,0] b=[b1,0,⋯,0]和序列 a a a,满足 a 1 = b 1 a_1=b_1 a1=b1,问 b b b能否进行若干次操作变为 a a a.操作:选择一个下标 2 ≤ i ≤ n 2\leq i\leq n 2≤i≤n,令 b i = b i + b i − 1 b_i=b_i+b_{i-1} bi=bi+bi−1.显然有解的充要条件是: a 2 ∼ a n a_2\sim a_n a2∼an都能被 a 1 a_1 a1整除.
代码
int main() {
CaseT{
int n; cin >> n;
vi a(n + 2);
for (int i = 1; i <= n; i++) cin >> a[i];
bool ok = true;
for (int i = 2; i <= n; i++) {
if (a[i] % a[1]) {
ok = false;
break;
}
}
cout << (ok ? "YES" : "NO") << endl;
}
}
Codeforces Round #808 (Div. 2) B. Difference of GCDs
原题指路:https://codeforces.com/contest/1708/problem/B
题意
给定区间 [ l , r ] [l,r] [l,r],构造一个长度为 n n n的序列 a 1 , ⋯ , a n ( l ≤ a i ≤ r ) s . t . gcd ( i , a i ) ( 1 ≤ i ≤ n ) a_1,\cdots,a_n\ \ (l\leq a_i\leq r)\ s.t.\ \gcd(i,a_i)\ \ (1\leq i\leq n) a1,⋯,an (l≤ai≤r) s.t. gcd(i,ai) (1≤i≤n)相异.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据输入三个整数 n , l , r ( 1 ≤ n ≤ 1 e 5 , 1 ≤ l ≤ r ≤ 1 e 9 ) n,l,r\ \ (1\leq n\leq 1\mathrm{e}5,1\leq l\leq r\leq 1\mathrm{e}9) n,l,r (1≤n≤1e5,1≤l≤r≤1e9).数据保证所有测试数据的 n n n之和不超过 1 e 5 1\mathrm{e}5 1e5.
对每组测试数据,若能构造出序列 a a a,则第一行输出"YES",第二行输出序列 a a a;否则输出一行"NO".
思路
因 gcd ( i , a i ) ≤ i \gcd(i,a_i)\leq i gcd(i,ai)≤i,而 gcd ( i , a i ) ( 1 ≤ i ≤ n ) \gcd(i,a_i)\ \ (1\leq i\leq n) gcd(i,ai) (1≤i≤n)相异,则 gcd ( i , a i ) = i \gcd(i,a_i)=i gcd(i,ai)=i,即 a i a_i ai是 i i i的倍数.
注意到 ≥ l \geq l ≥l的最小的 i i i的倍数是 t m p = ⌈ l i ⌉ ⋅ i tmp=\left\lceil\dfrac{l}{i}\right\rceil\cdot i tmp=⌈il⌉⋅i,故只需检查 t m p ≤ r tmp\leq r tmp≤r是否成立.
代码
int main() {
CaseT{
int n; cin >> n;
int l, r; cin >> l >> r;
if (n == 1) {
cout << "YES" << endl;
cout << l << endl;
continue;
}
vi ans(n + 2);
ans[1] = l;
int cnt = 1;
for (int i = 2; i <= n; i++) {
if (cnt >= n) break;
int tmp = ceil((double)l / i) * i;
if (tmp <= r && !ans[i]) {
ans[i] = tmp;
cnt++;
}
}
if (cnt != n) {
cout << "NO" << endl;
continue;
}
else {
cout << "YES" << endl;
for (int i = 1; i <= n; i++) cout << ans[i] << ' ';
cout << endl;
}
}
}
Codeforces Round #808 (Div. 2) C. Doremy’s IQ
原题指路:https://codeforces.com/contest/1708/problem/C
题意
你初始时的IQ为 q q q.现你要打 n n n场比赛,其中第 i i i场比赛的难度为 a i a_i ai,且只能在第 i i i天打.你能打比赛的前提是当前的 q > 0 q>0 q>0.在第 i i i天,你可选择打或不打当天的比赛:①若 a i > q a_i>q ai>q,则 q − − q-- q−−;②若 a i ≤ q a_i\leq q ai≤q,无事发生.求一个策略,使得你可以打尽量多场比赛.策略用一个长度为 n n n的二进制串表示, 1 1 1表示打当天的比赛, 0 0 0表示不打当天的比赛.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据第一行输入两整数 n , q ( 1 ≤ n ≤ 1 e 5 , 1 ≤ q ≤ 1 e 9 ) n,q\ \ (1\leq n\leq 1\mathrm{e}5,1\leq q\leq 1\mathrm{e}9) n,q (1≤n≤1e5,1≤q≤1e9).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 1 ≤ a i ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq1\mathrm{e}9) a1,⋯,an (1≤ai≤1e9).数据保证所有测试数据的 n n n之和不超过 1 e 5 1\mathrm{e}5 1e5.
对每组测试数据,任输出一个可打尽量多场比赛的策略.
思路I
称会降低 q q q的比赛为坏比赛,其余比赛为好比赛.
最优策略中存在一个下标$x\ s.t.\ ① ① ①[1,x) 中的好比赛都打了 ; ② 中的好比赛都打了;② 中的好比赛都打了;②[x,n]$中的比赛都打了.
[证] 对每个策略,设 a a a为最后一场没打的比赛, b b b为第一场打的坏比赛.
①若 b < a b<a b<a,则可不打 b b b,改打 a a a,这样打的比赛数不变.
②若 b > a b>a b>a,则 x = b x=b x=b即满足.
当 x x x最小时即为最优策略, x x x的最小值可二分.时间复杂度 O ( n log n ) O(n\log n) O(nlogn).
代码I
const int MAXN = 1e5 + 5;
int n, q;
int a[MAXN];
int ans[MAXN];
bool check(int m) {
for (int i = 1; i <= n; i++) ans[i] = 0;
int curq = q;
for (int i = 1; i < m; i++)
if (a[i] <= curq) ans[i] = 1;
for (int i = m; i <= n; i++) {
ans[i] = 1;
if (!curq) return false;
if (a[i] > curq)curq--;
}
return curq >= 0;
}
int main() {
CaseT{
cin >> n >> q;
for (int i = 1; i <= n; i++) cin >> a[i];
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
check(l); // 求策略
for (int i = 1; i <= n; i++) cout << ans[i];
cout << endl;
}
}
思路II
逆向考虑问题.初始时在最后一场比赛的后一天,且 c u r q = 0 curq=0 curq=0.
① a i ≤ c u r q a_i\leq curq ai≤curq时,则应打第 i i i场比赛.
② a i > c u r q a_i>curq ai>curq且 c u r q < q curq<q curq<q时,若打第 i i i场比赛,则 c u r q + + curq++ curq++;否则无事发生.
注意到最多能打 q q q场满足 a i > c u r q a_i>curq ai>curq的比赛,若能使 c u r q curq curq尽量大,就能打更多前面的好比赛.故应打第 i i i场比赛.
③ a i > c u r q a_i>curq ai>curq且 c u r q = q curq=q curq=q时,无法打第 i i i场比赛.
倒着决定每场比赛是否打即可.时间复杂度 O ( n ) O(n) O(n).
代码II
int main() {
CaseT{
int n, q; cin >> n >> q;
vi a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
int curq = 0;
vi ans(n + 1);
for (int i = n; i; i--) {
if (a[i] <= curq) ans[i] = 1;
else if (curq < q) curq++, ans[i] = 1;
else ans[i] = 0;
}
for (int i = 1; i <= n; i++) cout << ans[i];
cout << endl;
}
}