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

P3813 [FJOI2017]矩陣填數

def clu 滿足 傳送門 狀態 sizeof include 面積 tle

傳送門

矩陣很大,但是發現 $n$ 很小,從這邊考慮,對於一個一堆小矩陣放在一起的情況

技術分享圖片

考慮把每一塊單獨考慮然後方案再乘起來

但是這些奇怪的東西很不好考慮

所以暴力一點,直接拆成一個個小塊

技術分享圖片

但是這樣我們還要考慮到小矩形的限制,設 $f[i][S]$ 表示現在考慮第 $i$ 個小塊,小矩形的限制滿足的狀態為 $S$ 時的方案數

發現這些小塊不會跨過矩形,維護每個小塊的限制(即這個塊能填的最大的數)$Mx$,以及這個小塊填最大數時,能使哪些小矩形滿足限制 ($P$)

設小塊的面積為 $S$,那麽如果下一小矩形不填最大數,則轉移到 $f[i+1][S]$,貢獻方案數為 $(Mx[i+1]-1)^{S[i+1]}$

如果下一小矩形填最大數,則轉移到 $f[i+1][S|P[i+1]]$,貢獻為總方案數-不填最大數的方案數$Mx[i+1]^{S[i+1]}\ -\ (Mx[i+1]-1)^{S[i+1]}$

然後就是奇奇怪怪的離散化和預處理了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int
read() { int x=0,f=1; char ch=getchar(); while(ch<0||ch>9) { if(ch==-) f=-1; ch=getchar(); } while(ch>=0&&ch<=9) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2007,mo=1e9+7; int T,h,w,n,m; int X1[N],X2[N],Y1[N],Y2[N],v[N];
int xp[N],yp[N],tx,ty,tot; int tmp[N],t; int f[N][N],S[N],Mx[N],P[N]; inline bool pd(int x,int y,int k) { return x>=X1[k]&&x<=X2[k]&&y>=Y1[k]&&y<=Y2[k]; } //判斷以(x,y)右上角的小塊是否在矩形k中,因為離散化後小塊不可能跨過矩形所以可以這樣判斷 inline int ksm(int x,int y) { int res=1; while(y) { if(y&1) res=1ll*res*x%mo; x=1ll*x*x%mo; y>>=1; } return res; } int main() { //以下默認往右為大,往上為大 T=read(); while(T--) { memset(f,0,sizeof(f)); memset(P,0,sizeof(P)); xp[tx=1]=0; yp[ty=1]=0; tot=0; h=read(),w=read(),m=read(),n=read(); for(int i=1;i<=n;i++) { X1[i]=read(),Y1[i]=read(),X2[i]=read(),Y2[i]=read(),v[i]=read(); xp[++tx]=X1[i]-1,yp[++ty]=Y1[i]-1;//註意-1,邊界很重要,左下弄成開區間很重要! xp[++tx]=X2[i],yp[++ty]=Y2[i]; } xp[++tx]=h; yp[++ty]=w; sort(xp+1,xp+tx+1); sort(yp+1,yp+ty+1); for(int i=1;i<=tx;i++) tmp[i]=xp[i]; t=tx; tx=0; for(int i=1;i<=t;i++) if(i==1||tmp[i]!=tmp[i-1]) xp[++tx]=tmp[i];//離散化 for(int i=1;i<=ty;i++) tmp[i]=yp[i]; t=ty; ty=0; for(int i=1;i<=t;i++) if(i==1||tmp[i]!=tmp[i-1]) yp[++ty]=tmp[i];//離散化 for(int i=2;i<=tx;i++) for(int j=2;j<=ty;j++) { tot++; Mx[tot]=m; S[tot]=(xp[i]-xp[i-1])*(yp[j]-yp[j-1]);//小的邊界是不包含的,即區間是左開右閉的,下開上閉的 for(int k=1;k<=n;k++) if(pd(xp[i],yp[j],k)) Mx[tot]=min(Mx[tot],v[k]);//處理Mx for(int k=1;k<=n;k++) if(pd(xp[i],yp[j],k) && Mx[tot]==v[k])//處理P P[tot]|=(1<<k-1); } int mx=(1<<n)-1; f[0][0]=1;//DP for(int i=0;i<tot;i++) { int t1=ksm(Mx[i+1]-1,S[i+1]),t2=(ksm(Mx[i+1],S[i+1])-t1+mo)%mo; for(int j=0;j<=mx;j++) { if(!f[i][j]) continue; f[i+1][j|P[i+1]]=(f[i+1][j|P[i+1]]+1ll*f[i][j]*t2%mo)%mo;//此塊填最大數 f[i+1][j]=(f[i+1][j]+1ll*f[i][j]*t1%mo)%mo;//此塊不填最大數 } } printf("%d\n",f[tot][mx]); } }

P3813 [FJOI2017]矩陣填數