垃圾ACMer的暑假训练220717

垃圾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=0nCnkgk,则 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=0n(1)nkCnkfk.

[] ∑ 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=0n(1)nkCnkfk=k=0n(1)nkCnki=0kCnigi=i=0ngik=in(1)nkCnkCki

= ∑ 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=0ngik=in(1)nkk!(nk)!n!i!(ki)!k!=i=0ngik=in(1)nki!(ni)!n!Cnink

= ∑ 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=0nCnigik=inCnink(1)nkj=nk i=0nCnigij=0niCnij(1)j=i=0nCnigi(11)ni=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}={n1k1}+k{n1k},初始条件 { 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} {n1k1}种方案.

②将新元素放入一个现有的非空子集,有 k { n − 1 k } k\begin{Bmatrix}n-1 \\ k\end{Bmatrix} k{n1k}种方案.

[性质] 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=1mCmi{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=0mi!(mi)!(1)miin.

[证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=1mCmi{ni}i!=i=1mCmig(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=1m(1)miCmiin,移项即证.

[证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=0iCijFj.

由二项式反演: 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=0i(1)ijCijGj=j=0i(1)ijCijjn=j=0ij!(ij)!(i1)iji!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=0mi!(mi)!(1)miin.


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  (1n2e5),对所有 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=0mi!(mi)!(1)miin=i=0m(mi)!(1)mii!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) (fg)(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] MODifac[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  (1n1e7,1m1e5,1s150).第二行输入 ( 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  (0wi<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) (mi)种颜色共 ( n − i s ) (n-is) (nis)个.先视为可重排列,乘 n ! ( s ! ) i ( n − i s ) ! \dfrac{n!}{(s!)^i(n-is)!} (s!)i(nis)!n!. n n n个位置的前 i i i个部分都只有一种颜色,最后一个部分每个位置可取 ( m − i ) (m-i) (mi)种颜色,乘 ( m − i ) n − i s (m-i)^{n-is} (mi)nis.故 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(nis)!n!(mi)nis.

设有 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=icolor(1)jiCjifj,

​ 则 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!) ansii!=j=icolor(ji)!(1)ji(fjj!),用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  (2n100)的序列 a a a,问能否进行若干次(可能为 0 0 0次)操作,使得 a i = 0    ( 2 ≤ i ≤ n ) a_i=0\ \ (2\leq i\leq n) ai=0  (2in).操作:选择一个下标 i ∈ [ 2 , n ] i\in[2,n] i[2,n],令 a i − = a i − 1 a_i-=a_{i-1} ai=ai1,

t    ( 1 ≤ t ≤ 100 ) t\ \ (1\leq t\leq 100) t  (1t100)组测试数据.每组测试数据第一行输入一个整数 n    ( 2 ≤ n ≤ 100 ) n\ \ (2\leq n\leq 100) n  (2n100).第二行输入 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  (1ai1e9).

对每组测试数据,若能进行若干次(可能为 0 0 0次)操作,使得 a i = 0    ( 2 ≤ i ≤ n ) a_i=0\ \ (2\leq i\leq n) ai=0  (2in),则输出"YES";否则输出"NO".

思路I

有解的充要条件是: a 2 ∼ a n a_2\sim a_n a2an都能被 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 2in,令 b i = b i + b i − 1 b_i=b_i+b_{i-1} bi=bi+bi1.显然有解的充要条件是: a 2 ∼ a n a_2\sim a_n a2an都能被 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  (lair) s.t. gcd(i,ai)  (1in)相异.

t    ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t  (1t1e4)组测试数据.每组测试数据输入三个整数 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  (1n1e5,1lr1e9).数据保证所有测试数据的 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)  (1in)相异,则 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=ili,故只需检查 t m p ≤ r tmp\leq r tmpr是否成立.

代码
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 aiq,无事发生.求一个策略,使得你可以打尽量多场比赛.策略用一个长度为 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  (1t1e4)组测试数据.每组测试数据第一行输入两整数 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  (1n1e5,1q1e9).第二行输入 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  (1ai1e9).数据保证所有测试数据的 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 aicurq时,则应打第 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;
	}
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值