【ARC068F】Solitaire(dp,计数,思维)

首先发现那个双端队列一定长这样:

也就是说,这个队列中的数先单调递减,然后再单调递增,最小值为 1 1 1

现在考虑从双端队列中取数,那么当我们取到 1 1 1 这个数时,我们会在原来的双端队列中取到类似这样的两个数列:(分别用红、蓝表示)

那么红、蓝两数列的总长度为 k k k,剩下的就是绿色的一段,长度为 n − k n-k nk,我们每次可以从两端任意取,共取 n − k − 1 n-k-1 nk1 次,方案数为 2 n − k − 1 2^{n-k-1} 2nk1

所以我们只用考虑前 k k k 个数的取法,然后乘上 2 n − k − 1 2^{n-k-1} 2nk1 就好了。

由图像,我们看一下弹出序列需要满足什么条件:

  1. 首先第 k k k 位肯定是 1 1 1。(题目要求)

  2. k k k 个数,一定是由一个或两个单调递减的数列混合而成的(这两个数列就是图中的红、蓝两个数列),并且其中的一个单调递减的数列肯定包含 1 1 1 这个点(蓝色数列)。

  3. n − k n-k nk 个数,其最大值应小于某一个提到的单调数列的最小值。也就是绿色段的最大值小于红色段的最小值。

那我们不妨设 f ( i , j ) f(i,j) f(i,j) 表示已经取了前 i i i 个数,他们的最小值为 j j j 时的方案数。(满足上面的 3 3 3 个条件)

那么我们设选的这 i i i 个数分成的两个单调递减的序列分别为 A A A B B B,其中最小值 j j j A A A 中, B B B 中的最小值为 m i n n minn minn

设除了我们选的这 i i i 个数剩下的 n − i n-i ni 个数组成的集合为 S S S S S S 中最大的数为 m a x S maxS maxS

大概可以用这张图表示:

f ( i , j ) f(i,j) f(i,j) 的定义得, B B B 序列已经满足了第 3 3 3 个条件,即 m a x S < m i n n maxS<minn maxS<minn

我们现在要从 S S S 中选一个数,加入 A A A B B B 里面。

考虑构造:

  1. 假设加入的数 x x x 满足 x < j x<j x<j,那么我们就把它加到 A A A 的末尾,此时仍满足所有条件。所以 f ( i , j ) f(i,j) f(i,j) f ( i + 1 , 1 ) ∼ f ( i + 1 , j − 1 ) f(i+1,1)sim f(i+1,j-1) f(i+1,1)f(i+1,j1) 转移。

  2. 假设加入的数 x x x 满足 x ≥ j xgeq j xj,如果加入 A A A 中, A A A 就不满足单调递减的性质了,所以只能加入 B B B 中。

    然后发现只能把 S S S 中的最大值 m a x S maxS maxS 加入到 B B B 的队尾(而且必须得满足 m a x S ≥ j maxSgeq j maxSj,不然会在第一种情况中算重)。

    由于 m a x S maxS maxS S S S 中的最大的数,所以当 m a x S maxS maxS 加入 B B B 数列成为 B B B 数列的最小值后,仍大于 S S S 中的所有数,满足条件。

    所以 f ( i , j ) f(i,j) f(i,j) f ( i + 1 , j ) f(i+1,j) f(i+1,j) 转移。

    至于为什么 S S S 中除 m a x S maxS maxS 的某一个数 x x x 都不能加入 B B B 的队尾:因为加入后, B B B 中的最小值就变成了 x x x,而 S S S 中的最大值仍为 m a x S maxS maxS。此时 x < m a x S x<maxS x<maxS,不满足条件。

所以通过 f ( i , j ) f(i,j) f(i,j) 就可以向 f ( i + 1 , 1 ∼ j ) f(i+1,1sim j) f(i+1,1j) 转移了。

显然答案就是 2 n − k − 1 × ∑ i = 2 n − k + 2 f ( k − 1 , i ) 2^{n-k-1} imessumlimits_{i=2}^{n-k+2}f(k-1,i) 2nk1×i=2nk+2f(k1,i)

还是有很多细节的,详见代码:

#include<bits/stdc++.h>

#define N 2010
#define ll long long
#define mod 1000000007

using namespace std;

int n,k;
ll f[N][N];

ll poww(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=2;i<=n;i++) f[1][i]=1;
	for(int i=1;i<k-1;i++)
	{
		f[i+1][n-i+1]=f[i][n-i+1];
		for(int j=n-i;j>=2;j--)
			f[i+1][j]=(f[i+1][j+1]+f[i][j])%mod;
	}
	ll ans=0;
	for(int i=2;i<=n-k+2;i++)
		ans=(ans+f[k-1][i])%mod;
	if(k==1) ans=1;//特判1:k=1
	if(n-k-1>=0) printf("%lld
",ans*poww(2,n-k-1)%mod);
	else printf("%lld
",ans);//特判2:n=k
	return 0;
}
原文地址:https://www.cnblogs.com/ez-lcw/p/14448649.html