HDU 5471 Count the Grid【容斥原理】
阿新 • • 發佈:2019-01-06
題意:給一個h*w(h,w<=10000)的二維矩陣,給定其中n(n<=10)個子矩陣的最大值v(v<=1000),求組成這個矩陣的方案數。
分析:由於h*w太大,而n,v比較小,此題可以從n,v下手。首先將大矩形離散化,就把大矩形分成啦數量很少的小矩形。對子矩陣的重疊部分,如果最大值不同,自然是取較小值。如果最大值相同,則比較麻煩,這時解題關鍵,需要通過容斥原理求方案數。例如:子矩陣a和子矩陣b最大值相等為v0,有重疊部分,求滿足情況比較麻煩,可以求不滿足情況。不滿足情況為a不滿足方案+b不滿足方案-a和b同時不滿足方案數。(基於ab的矩形內的數都小於等於v0)ans=總方案數-不滿足情況,a不滿足情況為((v0-1)^(num[a])*(v0)^(num[a並b]-num[a]) ),由此可以推至多個,用狀壓儲存狀態。所以就要分別求最大值相等的小矩形的總方案數。最後答案是所有方案數相乘。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const LL MOD=1e9+7; struct node { int x1,y1,x2,y2,v; int siz() //返回面積 { return (x2-x1)*(y2-y1); } } a[20]; LL POW(LL x,LL n,LL MOD) { LL ret=1; while(n) { if(n&1)ret=ret*x%MOD; x=x*x%MOD; n>>=1; } return ret; } bool compare(const node&a,const node&b) { return a.x1<=b.x1&&a.y1<=b.y1&&a.x2>=b.x2&&a.y2>=b.y2; } int X[30],Y[30],V[30],id[30]; int jiao[12][4810],bing[12][4810]; vector<int>g[30]; int main() { int TA,h,w,m,n,hy,hx,hv,cas=1; scanf("%d",&TA); while(TA--) { memset(jiao,0,sizeof(jiao)); memset(bing,0,sizeof(bing)); scanf("%d%d%d%d",&h,&w,&m,&n); hx=hy=hv=0; X[++hx]=0; X[++hx]=w; Y[++hy]=0; Y[++hy]=h; for(int i=1; i<=n; i++) { scanf("%d%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2,&a[i].v); a[i].x1--; a[i].y1--; X[++hx]=a[i].x1; X[++hx]=a[i].x2; Y[++hy]=a[i].y1; Y[++hy]=a[i].y2; V[++hv]=a[i].v; } sort(X+1,X+hx+1); sort(Y+1,Y+hy+1); sort(V+1,V+hv+1); hx=unique(X+1,X+hx+1)-X-1; hy=unique(Y+1,Y+hy+1)-Y-1; hv=unique(V+1,V+hv+1)-V-1; for(int i=1; i<=n; i++) { a[i].v=lower_bound(V+1,V+hv+1,a[i].v)-V; id[i]=(int)g[a[i].v].size(); g[a[i].v].push_back(i); } int tm,siz; LL all,out=0; for(int i=1; i<hx; i++) //將小矩形歸類 for(int j=1; j<hy; j++) { node tem=(node) { X[i],Y[j],X[i+1],Y[j+1],hv+1 }; tm=0; for(int k=1; k<=n; k++) if(compare(a[k],tem)) { if(a[k].v<tem.v) { tem.v=a[k].v; tm=1<<id[k]; } else if(a[k].v==tem.v) { tm|=(1<<id[k]); } } siz=tem.siz(); if(tem.v>hv) out+=siz; else for(int k=tm; k; k=tm&(k-1))jiao[tem.v][k]+=siz; } for(int i=1; i<=hv; i++) //第一次容斥,求每個最大值所包含每種狀態的交集大小 { int tm=1<<(int)g[i].size(); for(int j=1; j<tm; j++) { for(int k=j; k; k=j&(k-1)) if(__builtin_popcount(k)&1) bing[i][j]+=jiao[i][k]; else bing[i][j]-=jiao[i][k]; } } LL sum,ans=POW((LL)m,out,MOD); for(int i=1; i<=hv; i++) //第二次容斥,求方案數 { int tm=(int)g[i].size(); tm=(1<<tm); all=bing[i][tm-1]; sum=0; for(int j=0; j<tm; j++) if(__builtin_popcount(j)&1) sum=(POW((LL)V[i]-1,(LL)bing[i][j],MOD)*POW((LL)V[i],all-bing[i][j],MOD)*(-1)+sum)%MOD; else sum=(POW((LL)V[i]-1,(LL)bing[i][j],MOD)*POW((LL)V[i],all-bing[i][j],MOD)+sum)%MOD; ans=ans*sum%MOD; } ans=(ans+MOD)%MOD; printf("Case #%d: %lld\n",cas++,ans); for(int i=0;i<=hv;i++)g[i].clear(); } return 0; }