CF1248C Ivan the Fool and the Probability Theory(计数DP+思维)

传送门
思路:
由题意可知:相邻两行要么相同,要么相反;

假设上述命题为假命题,
那么一定存在a[ i ][ j ] != a[ i ][ j+1 ] , a[ i+1 ][ j ] == a[ i+1 ][ j+1 ] ;
由于要么为黑、要么为白(每个格子只有两种状态)
则这四个格子中一定有一个格子有两个相同颜色的相邻格子,
与题目要求矛盾,
因此上述命题为真命题;
得证。

分析一下第一行的情况,如果第一行存在有相邻的两个格子颜色相同,那么第二行也一定相反,同理第三行和第二行相反。上述第一行的这种排列对ans的贡献值是1,即一种这样的排列代表一种合法矩阵。
设 f[ i ][ j ] ,j=0或者1
表示以颜色 j 结尾的长度为 i 的合法排列个数,
则可以得到
f[ i ][ 0 ] = f [ i - 1 ][ 1 ] + f[ i - 2][ 1 ]; 在…1后面补一个0或者两个0
f[ i ][ 1 ] = f [ i - 1 ][ 0 ] + f[ i - 2][ 0 ]; 同理
【也可以把上面两个递推式合并成一个(叠加)】
第一种的情况 为 f [ n ][ 0 ] + f[ n ][ 1 ] - 2 ,
-2 是因为 10101010101和 01010101010这两种序列不属于第一种情况需要减掉 (容斥原理)

第二种情况即为第一行为0101010或者1010101010 这两个排列:
此时第二行可以和第一行相同,也可以不同;
如果第二行和第一行相同,那么第三行必定不同;第二行与第一行不同,那么第三行可与第二行相同可以不同
……

这个时候其实只要确定行首元素就能够确定整个序列了,(要么是101010,要么是010101),即相当于计算第一列的合法排列数: f[ m ][ 1 ] + f[ m ][ 0 ];

AC代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1e6+5;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
#define ls (i<<1)
#define rs (i<<1|1)
#define fi first
#define se second
#define mk make_pair
#define mem(a,b) memset(a,b,sizeof(a))
LL read()
{
    LL x=0,t=1;
    char ch;
    while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
    while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
    return x*t;
}
LL f[N][2];
int main()
{
    LL n=read(),m=read();
    f[1][1]=f[1][0]=1;
    f[2][1]=f[2][0]=2;
    for(int i=3;i<=max(n,m);i++)
    {
        f[i][1]=f[i-1][0]+f[i-2][0];
        f[i][0]=f[i-1][1]+f[i-2][1];
        f[i][1]%=mod; f[i][0]%=mod;
    }
    printf("%lld\n",(f[n][0]+f[n][1]+f[m][0]+f[m][1]-2)%mod);
    return 0;
}

其实这道题数据范围应该能再大一点,然后用矩阵快速幂加速即可。

posted @ 2019-12-11 21:14  DeepJay  阅读(173)  评论(0编辑  收藏  举报