LOJ 3276 JOISC 2020 Day2 遗迹 题解 (计数DP)
Posted LegendStane
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LOJ 3276 JOISC 2020 Day2 遗迹 题解 (计数DP)相关的知识,希望对你有一定的参考价值。
观察一下n次地震的过程,发现最后会有n个石柱高度为0,\\(1,2\\cdots n\\)高度的石柱各有一个。假设现在已经确定了一种初始高度状态,我们来看看最后哪些石柱高度会大于0:
- 首先,初始高度为n的两个石柱中,较右的会留下来,最后高度为n的石柱就是它
- 接下来,最后高度为n-1的石柱是初始高度为n的两个石柱中较左的那个,以及初始高度为n-1的两个石柱中最右的那个
- 以此类推\\(\\cdots\\)
稍微推一下可以总结成以下过程:从大到小枚举每种初始高度,枚举到i时,把初始高度为i的两个石柱加入一个堆中,然后从堆中弹出最右的一个石柱,这个石柱就是最终高度为i的。最后堆中会剩下n个石柱,这些就是最后高度为0的。
知道了这个似乎还是不太好做。我们来看看在确定了初始状态后,能不能方便地确定每个石柱最后的高度。我们维护一个"剩余可用高度集合"S,初始\\(S=\\1,2\\cdots n\\\\)。令第i个石柱的初始高度为\\(h_i\\),我们从右到左遍历每个石柱i,并在S中找出\\(\\le h_i\\)的最大的元素。如果这个元素不存在,那i号石柱最终的高度就是0;否则最终的高度就是这个元素的值,我们需要把这个元素从S中删除。根据上面总结的过程,这部分也很好理解。用最右边的石柱举个例子吧,由于它在所有的"取出最右"的过程中都能杀穿,所以它一定能抢到最终高度为\\(h_2n\\)的位置。
现在题目钦定了每个石柱最终高度是0(被删除)还是大于0(不被删除),我们要求出满足这些条件的初始状态数。我们用dp来从右到左决定石柱的高度。一个合法的初始状态中每个高度都恰有2个石柱,为了便于dp,我们假设这两个石柱是不同的(相当于假设高度为i的石柱有红蓝两种,分配的时候任选一种不得重复),最后把答案除以\\(2^n\\)即可。令\\(dp_i,j\\)表示处理完了最靠右的i个石柱,S中的前j个元素都已经被删掉,第j+1个元素没被删掉(如果存在的话),且只决定了最右边的i个石柱中 钦定被删掉的那些 和 钦定没被删掉的那些中最终高度\\(\\le j\\)的那些 的初始高度,其它石柱初始高度待定,此时的方案数。这句话包含的条件有点多,建议多看几遍。令最右的i个石柱中有\\(cdel\\)个钦定被删除,\\(ckeep\\)个钦定不被删除。(主动)转移有以下几种情况:
- 当前石柱钦定被删掉。则它的初始高度必须\\(\\le j\\),否则它的最终高度就是j+1了。高度范围\\([1,j]\\)中一共有2j种石柱,钦定不被删的消耗了j种,钦定被删的消耗了\\(cdel\\)种,所以一共有\\(2j-j-cdel\\)种方案。因此,\\(dp_i+1,j+=dp_i,j\\cdot(j-cdel)\\)。
- 当前石柱钦定不被删掉,且选择初始高度后S被删除的连续前缀长度不变。那么我们就先不选它的初始高度,先待定,以后再选。\\(dp_i+1,j+=dp_i,j\\)。
- 当前石柱钦定不被删掉,且选择初始高度后S被删除的连续前缀变长了。我们枚举k,表示S被删除的连续前缀长度从j变成了\\(j+k\\)。要从S中删除这多出来的k个元素,需要k个石柱,当前石柱占了一个(它刚好从S中删掉了j+1,选择它的初始高度的方案数是\\(2k-(k-1)=k+1\\)),我们还需要从\\(ckeep-j\\)个初始高度待定的中选出\\(k-1\\)个,由于这k-1个之间是有顺序的,所以方案数\\(\\binomckeep-jk-1\\cdot (k-1)!\\)。还要乘上一个额外的方案数\\(f_k-1\\),其中\\(f_i\\)表示在\\(1-i\\)的高度值,\\(2i\\)种不同石柱中选出i个,使得删掉的S中的元素刚好为\\(1-i\\)的方案数(注意只是选出,选出元素之间没有顺序)。综上,\\(dp_i+1,j+k+=dp_i,j\\cdot (k+1)\\cdot \\binomckeep-jk-1\\cdot (k-1)!\\cdot f_k-1\\)。
上面这部分的复杂度是\\(O(n^3)\\)的。
然后来求一下f。令\\(dp2_i,j\\)表示处理了最大的i种高度,选了j个石柱的方案数。为了保证合法,需要满足\\(j总是\\ge i\\)(即j<i时dp值为0)。转移就枚举接下来这种高度选0、1还是2个即可。\\(f_i=dp2_i,i\\)。这部分时间复杂度\\(O(n^2)\\)。
总时间复杂度\\(O(n^3)\\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define fi first
#define se second
#define pb push_back
#define mpr make_pair
void fileio()
#ifdef LGS
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
void termin()
#ifdef LGS
std::cout<<"\\n\\nEXECUTION TERMINATED";
#endif
exit(0);
using namespace std;
const LL MOD=1e9+7;
LL qpow(LL x,LL a)
LL res=x,ret=1;
while(a>0)
if(a&1) (ret*=res)%=MOD;
a>>=1;
(res*=res)%=MOD;
return ret;
LL n,del[1210],dp2[610][610],f[610],dp[1210][610],fac[1210],inv[1210];
int main()
fileio();
freopen("temple.in","r",stdin);
freopen("temple.out","w",stdout);
fac[0]=1;repn(i,1205) fac[i]=fac[i-1]*i%MOD;
rep(i,1203) inv[i]=qpow(fac[i],MOD-2);
cin>>n;
rep(i,n+n+3) del[i]=1;
LL x;
rep(i,n)
scanf("%lld",&x);--x;
del[x]=0;
dp2[0][0]=1;
rep(i,n+1) for(int j=i;j<=min((LL)i+i,n);++j)
(dp2[i+1][j]+=dp2[i][j])%=MOD;
(dp2[i+1][j+1]+=dp2[i][j]*2)%=MOD;
(dp2[i+1][j+2]+=dp2[i][j])%=MOD;
rep(i,n+2) f[i]=dp2[i][i];
reverse(del,del+n+n);
dp[0][0]=1;
LL cdel=0,ckeep=0;
rep(i,n+n)
rep(j,ckeep+1) if(dp[i][j])
if(del[i])
if(j-cdel>=0)
(dp[i+1][j]+=dp[i][j]*(j-cdel))%=MOD;
else
(dp[i+1][j]+=dp[i][j])%=MOD;
repn(k,n-j)
LL lftkeep=ckeep-j;
if(k-1>lftkeep) break;
LL val=dp[i][j]*(k+1)%MOD;
if(k-1>0) (val*=fac[lftkeep]*inv[lftkeep-(k-1)]%MOD*f[k-1]%MOD)%=MOD;
(dp[i+1][j+k]+=val)%=MOD;
cdel+=del[i];ckeep+=(1-del[i]);
LL ans=dp[n+n][n];
(ans*=qpow(qpow(2,n),MOD-2))%=MOD;
cout<<ans<<endl;
termin();
以上是关于LOJ 3276 JOISC 2020 Day2 遗迹 题解 (计数DP)的主要内容,如果未能解决你的问题,请参考以下文章