zoukankan      html  css  js  c++  java
  • 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-1ge i,yge j),要满足((x,y))的值等于((x-1,y+1)),否则加上第一条性质,它就会满足(a_{x,y}=1,a_{x-1,y+1}=0),那可以有两条路径,分别为((1,1) o (i-1,j-1) o (i,j-1) o (i,j) o (x-1,y) o (x-1,y+1)),以及((1,1) o (i-1,j-1) o (i-1,j) o (i,j) o (x-1,y) o (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
    ",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
    ",(int)(4ll*ans*fpow(3,m-nn)%mod));
    	return 0;
    }
    
  • 相关阅读:
    原生JavaScript事件详解
    如何真正重写window对象的方法
    JSLint JavaScript代码质量审查工具汉化中文版隆重发布
    {{偷偷告诉你}}本博客已适配移动端浏览
    谷歌(Chrome)浏览器调试JavaScript小技巧
    小米Web前端JavaScript面试题
    根据配置文件加载js依赖模块(JavaScript面试题)
    中移杭州研发中心
    MyBatis与Hibernate区别
    hashmap源码解析,JDK1.8和1.7的区别
  • 原文地址:https://www.cnblogs.com/smyjr/p/11864781.html
Copyright © 2011-2022 走看看