Технокубок 2018 - Отборочный Раунд 3 E. Maximum Element DP

Description
求n的排列有多少满足:从左到右扫求最大值,当最大值不再变化k次后的最大值不为n。


Sample Input
5 2


Sample Output
22


这题氪了一波金。。。
其实有很多解决方法,在这里我提供两种。
首先定义合法为满足出现一个最大值它是假的。
第一种:
f [ i ] f[i] f[i]为填到第 i i i个数都是不合法的。
那么可以得到转移:
f [ i ] = ∑ j = 0 m i n ( k − 1 , i − 1 ) f [ i − j − 1 ] ∗ C ( i − 1 , j ) ∗ j c [ j ] f[i]=\sum_{j=0}^{min(k-1,i-1)}f[i-j-1]*C(i-1,j)*jc[j] f[i]=j=0min(k1,i1)f[ij1]C(i1,j)jc[j]
你可以化一下,变成: f [ i ] = ∑ j = 0 m i n ( k − 1 , i − 1 ) f [ i − j − 1 ] ∗ j c [ i − j − 1 ] ∗ j c [ i − 1 ] f[i]=\sum_{j=0}^{min(k-1,i-1)}f[i-j-1]*jc[i-j-1]*jc[i-1] f[i]=j=0min(k1,i1)f[ij1]jc[ij1]jc[i1]
这个 f [ i − j − 1 ] ∗ j c [ i − j − 1 ] f[i-j-1]*jc[i-j-1] f[ij1]jc[ij1]你可以用一个前缀和来优化它。
最后统计答案时,你枚举n出现的位置。
那么你要保证n前面是合法的,后面就随便填。
那不合法的方案就是: ∑ i = 1 n f [ i − 1 ] ∗ C ( n − 1 , i − 1 ) ∗ j c [ n − i ] \sum_{i=1}^nf[i-1]*C(n-1,i-1)*jc[n-i] i=1nf[i1]C(n1,i1)jc[ni]
用总数减掉即可。

第二种:
f [ i ] f[i] f[i]为填到第 i i i个数是合法的。
g [ i ] g[i] g[i]为填到第 i i i个数是不合法的。
那么你有两种情况的转移,第一种是前面已经构成合法方案:
f [ i ] = f [ i − 1 ] ∗ C ( i , i − 1 ) f[i]=f[i-1]*C(i,i-1) f[i]=f[i1]C(i,i1) => f [ i ] = f [ i − 1 ] ∗ i f[i]=f[i-1]*i f[i]=f[i1]i
第二种是当前这里构成了一个新和方案:
f [ i ] = g [ i − k − 1 ] ∗ C ( i − 1 , k ) ∗ j c [ k ] f[i]=g[i-k-1]*C(i-1,k)*jc[k] f[i]=g[ik1]C(i1,k)jc[k]
对于 g g g的转移直接用总数减去 f f f即可。

我写了第一种。


#include <cstdio>
#include <cstring>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
const LL mod = 1e9 + 7;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

LL inv[1000010], jc[1000010];
LL f[1000010], sum[1000010];

LL pow_mod(LL a, LL k) {
	LL ans = 1;
	while(k) {
		if(k & 1) (ans *= a) %= mod;
		(a *= a) %= mod; k /= 2;
	} return ans;
}

LL C(LL n, LL m) {return jc[n] * inv[m] % mod * inv[n - m] % mod;}

int main() {
	int n = read(), k = read();
	jc[0] = 1; for(int i = 1; i <= n; i++) jc[i] = (LL)jc[i - 1] * i % mod;
	inv[0] = 1; inv[n] = pow_mod(jc[n], mod - 2);
	for(int i = n - 1; i >= 1; i--) inv[i] = (LL)inv[i + 1] * (i + 1) % mod;
	sum[1] = 1; f[0] = 1;
	for(int i = 1; i <= n; i++) {
		f[i] = (sum[i] - sum[_max(0, i - k)]) % mod * jc[i - 1] % mod;
		(sum[i + 1] = sum[i] + f[i] * inv[i] % mod) % mod;
	} LL ans = 0;
	for(int i = 1; i <= n; i++) (ans += f[i - 1] * C(n - 1, i - 1) % mod * jc[n - i] % mod) % mod;
	printf("%lld\n", ((jc[n] - ans) % mod + mod) % mod);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值