• 填格子


    ### Description

      给你一个(2*m)的网格,每个格子必须涂成红绿蓝((RGB))中的一种,要求每个(2*2)的网格中每种颜色都要出现过,并且对于整个网格,任意两个相邻的格子颜色不能相同,现给出三种颜色的出现次数,输出合法的方案数对(10^9+7)取模

      数据范围:(m<=10^6)

      

    Solution

      首先注意到因为行数是(2)颜色数是(3),所以我们可以只考虑每一列没有出现过的颜色个数,记这个组成的序列为(A)

      因为(2*2)网格中所有的颜色都要出现过一次,所以(A)中的相邻元素一定不同,我们考虑其中一种颜色(R),显然(R)将整个(A)分成了若干段,每段只有(G)(B),考虑这个段数,总共只有三种情况:(R+1)(R)(R-1),计算的方式类似,我们可以计算三次然后把结果加起来

      假设现在(GB)的段数为(x),我们可以枚举其中长度为奇数的段总共有多少段,然后根据(G)(B)的出现次数的差,我们可以确定长度为奇数的段中,形如(GBG)的段比(BGB)的多多少

      然后我们就可以直接用组合数计算了,具体一点就是(假设(B>G)):

    [egin{aligned} tmp&=B-G\ ans&=sumlimits_{i=tmp}^x [(i+tmp)\%2==0]inom x icdotinom i{frac{i+tmp}{2}}cdotinom{frac{G+B+i}{2}-1}{x-1}cdot 2^{x-i} end{aligned} ]

      前面两个组合数算的是奇数段的,具体就是从(x)段中选出(i)段作为奇数段,然后为了满足(B)(G)的出现次数差,(BGB)的出现次数应该为(frac{i+tmp}{2}),后面算的是偶数段,为了保证每段都是偶数所以先集体除以(2)然后直接插板,因为有(GB)(BG)两种形式所以还要乘上一个(2^{x-i})

      最后统计答案的时候记得(R+1)(R-1)的方案数要乘(2),因为上下两行可交换;(R)的方案数要乘(4),因为除了上下两行可交换以外(R)的段和(GB)的段也可以交换位置

      

      代码大概长这个样子

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=1e6+10,MOD=1e9+7;
    int fac[N],invfac[N],pw2[N];
    int n,m,T,R,G,B;
    int ans;
    int mul(int x,int y){return 1LL*x*y%MOD;}
    int plu(int x,int y){return (1LL*x+y)-(1LL*x+y>=MOD?MOD:0);}
    int ksm(int x,int y){
    	int ret=1,base=x;
    	for (;y;y>>=1,base=mul(base,base))
    		if (y&1) ret=mul(ret,base);
    	return ret;
    }
    void prework(int n){
    	fac[0]=1;
    	for (int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
    	invfac[n]=ksm(fac[n],MOD-2);
    	for (int i=n-1;i>=0;--i) invfac[i]=mul(invfac[i+1],i+1);
    	pw2[0]=1;
    	for (int i=1;i<=n;++i) pw2[i]=mul(pw2[i-1],2);
    }
    int C(int n,int m){return n<m?0:mul(fac[n],mul(invfac[m],invfac[n-m]));}
    int calc(int r,int g,int b){
    	if (r<=0) return 0;
    	int ret=0,tmp=b-g;
    	for (int i=tmp;i<=r;++i){
    		if ((i+tmp)%2) continue;
    		ret=plu(ret,mul(C(r,i),mul(C(i,(i+tmp)/2),mul(C((g+b+i)/2-1,r-1),pw2[r-i]))));
    	}
    	return ret;
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	scanf("%d",&T);
    	prework(N-10);
    	for (int o=1;o<=T;++o){
    		scanf("%d%d%d%d",&n,&R,&G,&B);
    		R=n-R; G=n-G; B=n-B;
    		if (R>G) swap(R,G);
    		if (R>B) swap(R,B);
    		if (G>B) swap(B,G);
    		ans=mul(2,calc(R-1,G,B));
    		ans=plu(ans,mul(4,calc(R,G,B)));
    		ans=plu(ans,mul(2,calc(R+1,G,B)));
    		printf("%d
    ",ans);
    	}
    }
    
  • 相关阅读:
    debian下安装oracle10G
    【海洋女神原创】如何为工程添加一个Installshield不支持的语言艾泽拉斯之海洋女神翻译
    无论程序是否安装,都使setup.exe直接进入安装状态,不出现反安装;以及由此延伸出的同一安装程序允许在同一机器上安装多套软件的探讨
    个人收藏的一些资料(五)Installshield定制行为
    Installshield无人值守安装sqlserver脚本
    个人收藏的一些资料(二)MSI更新和补丁包的制作
    个人收藏的一些资料(三)MSI打包白皮书
    Installshield建立IE快捷方式的方法
    个人收藏的一些资料(一)Installshield制作友好的更新
    MyEclipse的自动提示太让人抓狂了,有没有什么办法改善?
  • 原文地址:https://www.cnblogs.com/yoyoball/p/10090178.html
Copyright © 2020-2023  润新知