1. 程式人生 > >[FJOI2017]矩陣填數

[FJOI2017]矩陣填數

problem mes ostream 轉移 log pro getc cpp main

[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());
}

[FJOI2017]矩陣填數