nomura2020_f Sorting Game
https://atcoder.jp/contests/nomura2020/tasks/nomura2020_f
Tutorial
https://img.atcoder.jp/nomura2020/editorial.pdf
https://atcoder.jp/contests/nomura2020/submissions/14860490
考虑当Snuke操作完之后,序列合法的条件就是,不存在无法交换的逆序对,也就是,不存在(x<y)满足(a_x>a_y)且(a_x,a_y)的二进制位相差超过一个位置.称此为条件1
考虑对于Snuke而言,有效的操作序列只有(i,i+1,cdots,N).因为对于(x<y),若对(j<i-1)操作,假如((j+1,i))区间内(a_x,a_y)二进制相等,那么等价于(j,j+1,cdots,N),否则(a_x,a_y)的大小关系不变,不同的二进制位置减少,对Takahashi来说是更优的.那么在Snuke操作前合法的(a)序列满足对于每种有效操作序列满足条件1,称其为条件2
观察所有数第(i)位,假如第(i)位之前都满足条件2,分为两种情况.
-
没有1在0之前,此时条件2依然满足.
-
存在1在0之前,观察最左边的1和最右边的0对应的数,首先它们的前(i-1)位应该相等,考虑在它们之间的数,若其第(i)位是0,那么它的前(i-1)位也应和最左边的1相同,若其第(i)位为1,那么如果它前(i-1)位与最左边的1不同,那么最右边的0的前(i)位也就与它不同;综上它们之间的所有数前(i-1)位相同.
用DP统计,设(dp(i,j))表示(n=i,m=j)时的答案,第一种情况就枚举0,1分界点的位置,第二种情况就枚举一个长度为(k<j)的序列,选择其中的一个数复制(j-k)次,这(j-k+1)个数满足第(i)位,它们中最左边的数为1,它们的左边的数为0,它们中最右边的数为0,它们右边的数为1,其他的(j-k-1)个数任意.
Code
#include <cstdio>
#include <iostream>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
inline char gc() {
// return getchar();
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void rd(T &x) {
x=0; int f=1,ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
x*=f;
}
typedef long long ll;
const int mod=1e9+7;
const int maxn=5000+5,maxm=5000+5;
int n,m;
int pow2[maxm],inv2[maxm];
int dp[maxn][maxm],sum[maxn][maxm];
int main() {
rd(n),rd(m);
pow2[0]=1,pow2[1]=2;
inv2[0]=1,inv2[1]=(mod+1)>>1;
for(int i=2;i<=m;++i) {
pow2[i]=(ll)pow2[i-1]*pow2[1]%mod;
inv2[i]=(ll)inv2[i-1]*inv2[1]%mod;
}
for(int i=1;i<=m;++i) {
dp[1][i]=pow2[i];
sum[1][i]=(sum[1][i-1]+(ll)dp[1][i]*i%mod*inv2[i])%mod;
}
for(int i=2;i<=n;++i) for(int j=1;j<=m;++j) {
dp[i][j]=((ll)dp[i-1][j]*(j+1)+(ll)sum[i-1][j-1]*pow2[j-1])%mod;
sum[i][j]=(sum[i][j-1]+(ll)dp[i][j]*j%mod*inv2[j])%mod;
}
printf("%d
",dp[n][m]);
return 0;
}