【题解】 CF802H Fake News (medium) 构造+贪心+组合数学

Legend

Link \(\textrm{to Codeforces}\)

给定 \(n\),求两个字符串 \(s,t\),使得 \(t\)\(s\) 中作为子序列出现了恰好 \(n\) 次。

\(1 \le n \le 10^6\)\(|s|,|t| \le 200\)

Editorial

我想起有一道题,它是构造 \(1447\) 作为子序列的个数。也是 cf 的。

我就想啊,能不能用三个字符也做这道题呢?

那道 \(1447\) 的题限制字符串长度比较宽松,所以做法就是:

\(14444444444\cdots444\)

然后把一堆的 \(7\) 贪心从右往左插入,如果左边有 \(k\)\(4\),那么就贡献了 \(\binom{k}{2}\)

受到这个做法的启发,我们来看这个题。

它没有限制组合数的下面一定是 \(2\),也就是 \(\binom{k}{2} \to \binom{k}{x}\),而我们可以任意选取这个 \(x\)

经过一番打表……(打表代码放在最后)

x = i
cnt 为估计最坏情况次数
mxneed 表示连续 '4' 段的长度最长是多少

i = 3 cnt = 366 mxneed = 182
i = 4 cnt = 145 mxneed = 71
i = 5 cnt = 91 mxneed = 43
i = 6 cnt = 70 mxneed = 32
i = 7 cnt = 63 mxneed = 27
i = 8 cnt = 58 mxneed = 24
i = 9 cnt = 59 mxneed = 23
i = 10 cnt = 59 mxneed = 22
i = 11 cnt = 62 mxneed = 22
i = 12 cnt = 62 mxneed = 22
i = 13 cnt = 65 mxneed = 22
i = 14 cnt = 67 mxneed = 23

我们可以发现大约 \(x\)\(x=8\) 的时候是相当优秀的,可以通过这个题。

复杂度不会算,但是显然可以过。

Code

代码非常好写,只需要预处理组合数即可。

#include <bits/stdc++.h>

int C[1000][1000];

void init(){
	for(int i = 0 ; i < 1000 ; ++i) C[i][0] = 1;
	for(int i = 1 ; i < 1000 ; ++i){
		for(int j = 1 ; j < 1000 ; ++j){
			C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
		}
	}
	int cnt = 0 ,mxneed;
	for(int i = 3 ; i <= 100 ; ++i){
		cnt = 0 ,mxneed = 0;
		for(int j = 1 ; C[j][i] <= 1e6 ; ++j){
			mxneed = j;
			if(C[j - 1][i]) cnt += ((C[j][i] - 1 - C[j - 1][i]) / C[j - 1][i]) + 1;
			
		}
		// printf("i = %d cnt = %d mxneed = %d\n" ,i ,cnt + 2 + mxneed ,mxneed);
	}
}

int pos[1000];

int main(){
	init();
	putchar('a');
	int n; std::cin >> n;
	for(int i = 24 ; n ; --i){
		int cnt = n / C[i][8];
		pos[i] = cnt;
		n -= cnt * C[i][8];
	}
	for(int i = 1 ; i <= 24 ; ++i){
		putchar('b');
		while(pos[i]--) putchar('c');
	}
	puts(" abbbbbbbbc");
}
posted @ 2020-09-26 17:06  Imakf  阅读(159)  评论(0编辑  收藏  举报