【HDU】P5471 Count the Grid
状压dp+容斥
由于n各集合有\(2^n\)个交集,因此我们可以把整个矩形划分成许多含有若干矩阵交集的子矩阵。
就像第一个样例就可以这样划分。
这样的话,我们就不用讨论许多矩阵相交的情况,问题就会简化很多。
设\(mx[i]\)表示第i个子矩阵内的最大值, \(S[i]\) 表示第i个子矩阵内含有的元素数量
然后,我们考虑状压dp。对于一个子矩阵,我们有如下决策:
1.子矩阵内的最大值小于$mx[i] $,此时方案数为 \((mx[i]-1)^{S[i]}\) 。
由于子矩阵是许多小矩阵的交集,只要一个小矩阵最大值达到了 \(mx[i]\),那么当前子矩阵实际最大值也为 \(mx[i]\),也是合法的。
2.子矩阵内的最大值为\(mx[i]\) ,此时方案数为 \(mx[i]^{S[i]}-(mx[i]-1)^{S[i]}\) 。由于子矩阵是许多小矩阵的交集,因此当前取到最大值可能会影响到一些其它小矩阵。
那么,\(dp[i][j]\) 表示前i个子矩阵,取得最大值的小矩阵状态为j的方案数
状态转移:
\[dp[i][j]=dp[i][j]+dp[i-1][j] \cdot (mx[i]-1)^{S[i]}
\]
\[dp[i][j|can[i]]=dp[i][j|can[i]]+dp[i-1][j]\cdot mx[i]^{S[i]}-(mx[i]-1)^{S[i]}
\]
其中\(can[i]\)表示第i个子矩阵最大值取到 \(mx[i]\) 会使得哪些小矩阵达到最大值
而对于划分成许多子矩阵我们可以用容斥预处理一波。
注意:所有小矩阵的并集可能不为整个矩阵,也就是说,这种情况下,有若干个点是可以随便取的(就像样例1),我们最后还要乘以\(m^{cnt}\)(\(cnt\)表示可以随便取的点数量)
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int T,h,w,m,n,dp[(1<<10)+5][(1<<10)+5],MM[(1<<10)+5],cas,sum=0;
const int MOD=1e9+7;
struct Set{
int x1,y1,x2,y2,mx,SS;
int S(){
return max(x2-x1+1,0LL)*max(y2-y1+1,0LL);
}
}A[10010],B[10010];
Set operator + (Set X,Set Y){
Set Ans;
Ans.mx=min(X.mx,Y.mx);
Ans.x1=max(X.x1,Y.x1);
Ans.y1=max(X.y1,Y.y1);
Ans.x2=min(X.x2,Y.x2);
Ans.y2=min(X.y2,Y.y2);
return Ans;
}
int Quick_Pow(int a,int p){
int res=1;
while(p){
if(p&1)res=res*a%MOD;
a=a*a%MOD;
p>>=1;
}
return res;
}
signed main(){
scanf("%lld",&T);
while(T--){
memset(dp,0,sizeof(dp));
memset(A,0,sizeof(A));
memset(B,0,sizeof(B));
memset(MM,0,sizeof(MM));
scanf("%lld %lld %lld %lld",&h,&w,&m,&n);
A[0]=Set{1,1,h,w,2e9+7,0};
for(int i=1;i<=n;i++){
scanf("%lld %lld %lld %lld %lld",&B[(1<<(i-1))].x1,&B[(1<<(i-1))].y1,&B[(1<<(i-1))].x2,&B[(1<<(i-1))].y2,&B[(1<<(i-1))].mx);
}
for(int i=1;i<(1<<n);i++)A[i]=A[i-(i&-i)]+B[i&-i];
for(int i=(1<<n)-1;i>=0;i--){
A[i].SS=A[i].S();
for(int j=i+1;j<(1<<n);j++){
if((j|i)==j)A[i].SS-=A[j].SS;
}
for(int j=1;j<=n;j++){
if((1<<(j-1))&i){
if(A[i].mx==A[(1<<(j-1))].mx){
MM[i]|=(1<<(j-1));
}
}
}
}
dp[0][0]=1;
for(int i=1;i<(1<<n);i++){
for(int j=0;j<(1<<n);j++){
dp[i][j]+=dp[i-1][j]*Quick_Pow(A[i].mx-1,A[i].SS)%MOD,dp[i][j]%=MOD;
dp[i][j|MM[i]]+=dp[i-1][j]*(Quick_Pow(A[i].mx,A[i].SS)-Quick_Pow(A[i].mx-1,A[i].SS)+MOD)%MOD,dp[i][j|MM[i]]%=MOD;
}
}
sum=h*w;
for(int i=1;i<(1<<n);i++)sum-=A[i].SS;
printf("Case #%lld: %lld\n",++cas,dp[(1<<n)-1][(1<<n)-1]*Quick_Pow(m,sum)%MOD);
}
return 0;
}