• nomura2020_f Sorting Game


    nomura2020_f Sorting Game

    https://atcoder.jp/contests/nomura2020/tasks/nomura2020_f

    UPmbqK.png

    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. 没有1在0之前,此时条件2依然满足.

    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)个数任意.

    [dp(i,j)=dp(i-1,j)(j+1)+sum_{k<j} dp(i-1,k)k cdot 2^{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;
    }
    
  • 相关阅读:
    其他魔术方法
    类型约束和类的魔术常量
    PHP对象遍历、内置标准类与数据转对象
    类的自动加载与对象的克隆
    PHP重载与接口
    面向对象
    PHP基础
    地下城与勇士的项目整理
    mysql建表
    jQuery
  • 原文地址:https://www.cnblogs.com/ljzalc1022/p/13255229.html
Copyright © 2020-2023  润新知