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;