luogu P5023 填數遊戲

luogu

loj

被這道題送退役了

題是挺有趣的,然而可能討論比較麻煩,肝了2h 又自閉了,鑑於CSP在即,就只能先寫個打表題解了

下面令\(n<m\),首先\(n=1\)時答案爲\(2^m\),然後打表可以發現,\(\forall i>n+1\ ans_{n,i}=3^{m-(n+1)}ans_{n,n+1}\),現在考慮怎麼快速打表

下面記從上往下行編號從\(1\)\(n\),從左往右列編號從\(1\)\(m\).要發掘兩個性質,第一個是對於一條左下到右上的對角線,填的數一定是先一段1再加上一段0.否則他就會存在一個爲0的位置\((i,j)\),滿足右上方格子\((i-1,j+1)\)是1,這個時候會有兩條從\((1,1)\)\((i-1,j)\)的重合路徑,下一步一條往下走,一條往又走,然後就導致往下走的路徑字典序小於往右走的字典序,導致不合法.還有一個性質是如果位置\((i,j)\)滿足\((i-1,j)\)格子值等於\((i,j-1)\),那麼\(\forall x,y\ x-1\ge i,y\ge j\),要滿足\((x,y)\)的值等於\((x-1,y+1)\),否則加上第一條性質,它就會滿足\(a_{x,y}=1,a_{x-1,y+1}=0\),那可以有兩條路徑,分別爲\((1,1)\to (i-1,j-1)\to (i,j-1)\to (i,j)\to (x-1,y)\to (x-1,y+1)\),以及\((1,1)\to (i-1,j-1)\to (i-1,j)\to (i,j)\to (x-1,y)\to (x,y)\),前者的字典序是應該大於等於後者的,但是這裏顯然不成立.可以發現滿足這兩個性質的方案都是滿足要求的

根據這兩條性質,我們就可以依次枚舉每條左下到右上的對角線,填了多少個1和0.接着考慮第二個性質,我們填數的時候就維護所有\((i,j)\)滿足\((i-1,j)\)格子值等於\((i,j-1)\)的位置,然後因爲一條對角線只會有一個位置\(a_{x,y}=1,a_{x-1,y+1}=0\),所以使得\((x-1,y)\)左上方矩形內沒有上述的\((i,j)\)就行了,這裏可以維護一個前綴最小值表示滿足\(j \le y\)的位置中上述\((i,j)\)\(i\)最小值.這樣可以在\(200ms\)左右打出\(ans_{8,9}\)

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=1e6+10,mod=1e9+7;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
void ad(int &x,int y){x+=y,x-=x>=mod?mod:0;}
int fpow(int a,int b){int an=1;while(b){if(b&1) an=1ll*an*a%mod;a=1ll*a*a%mod,b>>=1;} return an;}
int n,nn,m,ans,b[10];
bool a[10][10];
void dfs(int x,int y)
{
    if(y==n){++ans;return;}
    int bb[10];
    memcpy(bb,b,sizeof(b));
    for(int i=x,j=y;i>=1&&j<=n;--i,++j)
    {
        a[i][j]=0;
        if(i<x&&j>y&&i>1&&j<n&&a[i-1][j]==a[i][j-1]) b[j]=min(b[j],i);
    }
    for(int j=1;j<=n;++j) b[j]=min(b[j],b[j-1]);
    x==nn?dfs(x,y+1):dfs(x+1,y);
    for(int i=x,j=y;i>=1&&j<=n;--i,++j)
    {
        a[i][j]=1;
        if(i==1||j==n||b[j]>=i)
            x==nn?dfs(x,y+1):dfs(x+1,y);
    }
    memcpy(b,bb,sizeof(bb));
}

int main()
{
    n=rd(),m=rd();
    if(n>m) swap(n,m);
    if(n==1){printf("%d\n",fpow(2,m));return 0;}
    for(int i=0;i<=n;++i) b[i]=m+1;
    nn=min(m,n+1);
    dfs(2,1);
    printf("%d\n",(int)(4ll*ans*fpow(3,m-nn)%mod));
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章