AT2301 Solitaire

传送门

这里提供智障的\(O(n^2)\)做法
其实是有\(O(logn)\)做法的,但是我太菜了想不出来

Solution:

首先可以发现生成的序列一定是一个两边向中间单调递减的序列
这样就可以发现我们的删除序列也是一个有两个单调递减的子序列的序列
然后我们就可以设\(f[i][j]\)为当前确定了\(i\)个数,最小值为\(j\)的方案数
然后我们发现这两个序列中有一个序列的最后一个元素就是\(1\),设这个序列为\(A\),则另一个序列为\(B\)
然后有一个性质:对于\(f[i][j]\),这个数如果加进\(A\),那么它就要小于\(j\)(因为\(A\)最后一个数得是\(1\),最小的数一定在\(A\)里),如果加进\(B\)序列,那么它一定要是剩下的数中最大的(思考一下就知道了,实在不行就模拟一下)
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
void read(int &x) {
    char ch; bool ok;
    for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=2010,mod=1e9+7;
int n,k,ans,f[maxn][maxn],sum[maxn];
int mul(int x,int y){return 1ll*x*y-1ll*x*y/mod*mod;}
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int del(int x,int y){return x-y<0?x-y+mod:x-y;}
int mi(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=mul(ans,a);
        b>>=1,a=mul(a,a);
    }
    return ans;
}
int main(){
    read(n),read(k);f[0][n+1]=1;
    for(rg int i=1;i<=k;i++){
        sum[n+1]=f[i-1][n+1];
        for(rg int j=n;j;j--)sum[j]=add(sum[j+1],f[i-1][j]);
        for(rg int j=1;j<=n-i+1;j++)f[i][j]=add(f[i][j],sum[j]);
    }
    printf("%d\n",mul((del(f[k][1],f[k-1][1])),mi(2,n-k-1)));
}
posted @ 2019-04-23 18:26 蒟蒻--lichenxi 阅读(...) 评论(...) 编辑 收藏