TC13986SubRectangles

TC13986SubRectangles

首先不难看出把前\(h-1\)行和前\(w-1\)列看作主元,其他的都可以通过主元表示出来。

通过手算可以发现一个很有用的结论\(a[i][j]=a[i\mod h][j]+a[i][j\mod w]-a[i\mod h][j\mod w]\)。

然后可以考虑填出左上角那个\(h\times w\)的矩形,然后填出前\(h\)行,前\(w\)列剩余元素。

然后需要满足对于左上角每一个元素都必须存在这一行/这一列和它间隔\(w/h\)的格子全部和它相同。

考虑给每一个位置分配一个类别\(A,B,C\)。

\(A:\)行相同

\(B:\)列相同

\(C:\)都相同

考虑容斥,枚举\(A,B,C\)的集合,表示\(C\)的列别至少是\(Cmask\)。

考虑一个真实的ABC方案被算的次数,刚好就是\(\sum_{i=0}^C2^{C-i}{C\choose i}(-1)^{i}=1^C=1\)​。

然后算一下系数就行了。

时间复杂度\(O(3^{hw}(h+w))\)。

code:

#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
//    int x=0;
//    char ch=getchar();
//    while(ch<'0'||ch>'9'){
//        ch=getchar();
//    }
//    while(ch>='0'&&ch<='9'){
//        x=(x<<1)+(x<<3)+(ch^48);
//        ch=getchar();
//    }
//    return x;
//}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int coef1[5][2],coef2[5][2];
int c[5][5];
int cnt1[5],cnt2[5];
const int MOD=1e9+7;
int quick(int A,int B){
	int res=1;
	while(B){
		if(B&1) res=1ll*res*A%MOD;
		A=1ll*A*A%MOD;
		B>>=1;
	}
	return res;
}
void add(int & A,int B){
	A+=B;
	if(A>=MOD) A-=MOD;
}
int gmask1[5],gmask2[5];
class SubRectangles{
	public:
		int countWays(int H, int W, int h, int w){
			int n=h*w;
			rb(i,0,4) c[i][0]=1;
			rb(i,1,4) rb(j,1,4) c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
			rep(i,w) rep(j,h) gmask1[i]|=1<<(j*w+i);
			rep(i,h) rep(j,w) gmask2[i]|=1<<(i*w+j);
			rep(i,h+1){
				int z=(W+w-1)/w-1;
				rep(k,h-i+1)
					add(coef1[i][0],quick(c[h-i][k],z+1));
				if(z){
					--z;
					rep(k,h-i+1)
						add(coef1[i][1],quick(c[h-i][k],z+1));
				}
			}
			rep(i,w+1){
				int z=(H+h-1)/h-1;
				rep(k,w-i+1)
					add(coef2[i][0],quick(c[w-i][k],z+1));
				if(z){
					--z;
					rep(k,w-i+1)
						add(coef2[i][1],quick(c[w-i][k],z+1));
				}
			}
			int ans=0;
			rep(maskb,1<<n){
				for(int maskc=maskb;;maskc=(maskc-1)&maskb){
					int tmp=1;
					int A=(1<<n)-1;
					A^=maskb;
					int B=maskb^maskc;
					int C=maskc;
					rep(j,w){
						int cnt=(A|C)&gmask1[j];
						cnt=__builtin_popcount(cnt);
						if(j<=(W-1)%w){
							tmp=1ll*tmp*coef1[cnt][0]%MOD;
						}
						else{
							tmp=1ll*tmp*coef1[cnt][1]%MOD;
						}
					}
					rep(i,h){
						int cnt=(B|C)&gmask2[i];
						cnt=__builtin_popcount(cnt);
						if(i<=(H-1)%h){
							tmp=1ll*tmp*coef2[cnt][0]%MOD;
						}
						else{
							tmp=1ll*tmp*coef2[cnt][1]%MOD;
						}
					}
					rep(i,__builtin_popcount(C)){
						tmp=(tmp<<1)%MOD;
					}
//					cout<<A<<' '<<B<<" "<<C<<" "<<tmp<<endl;
					if(__builtin_popcount(maskc)&1) add(ans,MOD-tmp);
					else add(ans,tmp);
					if(!maskc) break;
				}
			}
			return ans;
		}
}solver;

上一篇:Bresenham算法画椭圆和斜椭圆


下一篇:32位指令格式——示例(一)