[FJOI2017]矩阵填数

[Luogu3813] [LOJ2280]

写得很好的题解

\(1.\)离散化出每一块内部不互相影响的块

\(2.\)\(dp[i][j]\)为前 \(i\) 种重叠块其中有 \(j\) 这些状态的矩阵的最大值被满足了的方案数 , 这样转移就之和这个块有关了 , 直接计算取最大值和不取的方案数即可

则当取最大值时,把对应方案数转移到 \(dp[i + 1][j | s[i + 1]]\),否则转移到 \(dp[i + 1][j]\)

\(dp[Bcnt][(1 << n) - 1]\)为最终的方案

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
#define y1 Y1
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
    register LL x=0,f=1;register char c=getchar();
    while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
    while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f*x;
}

const int N=10005;
const int M=31;
const int mod=1e9+7;

int x1[M],x2[M],y1[M],y2[M],val[M];
int x[M],y[M],block[M*M],st[M*M],f[M*M][1100],blimit[M*M];
int n,m,Val,K,Xcnt,Ycnt,Bcnt;

namespace Math{
    inline int add(int x,int y){x+=y;return x>=mod?x-mod:x;}
    inline int dec(int x,int y){x-=y;return x<0?x+mod:x;}
    inline int mul(LL x,int y){x*=y;return x>=mod?x%mod:x;}
    inline int qpow(int a,int b){
        int res=1;
        while(b){
            if(b&1) res=mul(res,a);
            a=mul(a,a);
            b>>=1;
        }
        return res;
    }
}using namespace Math;

inline bool in(int x,int y,int i){
    return x>=x1[i]&&x<=x2[i]&&y>=y1[i]&&y<=y2[i];//判断(x,y)是否在i这个矩形里面
}

inline void init(){
    memset(f,0,sizeof f);
    memset(st,0,sizeof st);
    x[Xcnt=0]=0;
    y[Ycnt=0]=0;
    Bcnt=0;
}

inline int solve(){
    n=read(),m=read(),Val=read(),K=read();
    x[++Xcnt]=0;//一定要记得放一个0!!!
    y[++Ycnt]=0;
    for(int i=1;i<=K;i++){
        x1[i]=read(),y1[i]=read(),x2[i]=read(),y2[i]=read(),val[i]=read();
        x[++Xcnt]=x1[i]-1;
        x[++Xcnt]=x2[i];
        y[++Ycnt]=y1[i]-1;
        y[++Ycnt]=y2[i];
    }
    x[++Xcnt]=n;
    y[++Ycnt]=m;
    sort(x+1,x+Xcnt+1);
    sort(y+1,y+Ycnt+1);
    Xcnt=unique(x+1,x+Xcnt+1)-x-1;
    Ycnt=unique(y+1,y+Ycnt+1)-y-1;

    for(int i=2;i<=Xcnt;i++)
        for(int j=2;j<=Ycnt;j++){
            block[++Bcnt]=(x[i]-x[i-1])*(y[j]-y[j-1]);
            blimit[Bcnt]=Val;
            for(int k=1;k<=K;k++){
                if(in(x[i],y[j],k)) blimit[Bcnt]=min(blimit[Bcnt],val[k]);//首先用最小的来约束它
            }
            for(int k=1;k<=K;k++){
                if(in(x[i],y[j],k)&&blimit[Bcnt]==val[k]) st[Bcnt]^=1<<(k-1);//初始化,统计出已经满足了哪些要求
            }
        }
    
    f[0][0]=1;
    for(int i=1;i<=Bcnt;i++){
        //由于是这样一块一块转移,每次只需要考虑这一块里面的
        int ful=st[i];
        LL fail=qpow(blimit[i]-1,block[i]);
        LL success=dec(qpow(blimit[i],block[i]),fail);//这一块取到最大值的方案
        for(int j=0;j<(1<<K);j++){
            f[i][j]=add(f[i][j],mul(f[i-1][j],fail));
            f[i][j|ful]=add(f[i][j|ful],mul(f[i-1][j],success));
        }
    }
    return f[Bcnt][(1<<K)-1];
}

int main(){
    for(int i=read();i;i--) init(),printf("%d\n",solve());
}

转载于:https://www.cnblogs.com/lizehon/p/10595023.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值