【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;
}

猜你喜欢

转载自www.cnblogs.com/SillyTieT/p/11457530.html
今日推荐