【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;
}
posted @ 2019-09-04 10:37  TieT  阅读(152)  评论(0编辑  收藏  举报