shadowice1984说:看到计数想容斥........
这题中,我们把图分成若干块,每块的最大值域不同
蓝后根据乘法原理把每块的方案数(互不相干)相乘。
怎么计算每块方案数呢
把子矩阵按最大值从小到大排序
暴力预处理好每$k(1<=k<=n)$个矩阵之间的交集、并集
设最大值为$v$的位置的集合大小为$S$
那么我们算出来(本层)的结果就是v^{|S|}
然鹅我们并没有减去某些矩阵没有到最大值(最大只取到$v-1$)的情况
于是对于每个最大值为$v$的子矩阵在$S$中的部分$T$,我们要减去$(v-1)^{|T|}*v^{|S-T|}$
但是我们又发现,多减去了某2个子矩阵都没有到最大值的情况,于是我们就把它加起来
再减去3个子矩阵都没到最大值的情况
.............
这就是容斥原理辣
“最大值为$v$的位置的集合大小为$S$”,这个咋算呢
$|S|=($最大值$<=v$的位置的集合大小)$-$(最大值$<v$的集合大小),$T$同理。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int mod=1e9+7; inline ll Min(ll a,ll b){return a<b?a:b;} inline ll Max(ll a,ll b){return a>b?a:b;} ll Pow(ll x,ll y){ ll re=1; for(;y;y>>=1,x=x*x%mod) if(y&1) re=re*x%mod; return re; } struct Matrix{ ll xl,yl,xr,yr,v; bool operator < (const Matrix &tmp) const{return v<tmp.v;} void operator &= (const Matrix &tmp) { xl=Max(xl,tmp.xl), xr=Min(xr,tmp.xr); yl=Max(yl,tmp.yl), yr=Min(yr,tmp.yr); } inline void init(){ scanf("%lld%lld%lld%lld%lld",&xl,&yl,&xr,&yr,&v); } inline bool null(){return xl>xr||yl>yr;} inline ll area(){return (xr-xl+1)*(yr-yl+1);} }a[20],p; int T,m,n,uni[2050],ist[2050],siz[2050];ll h,w; void Solve(){ memset(uni,0,sizeof(uni)); scanf("%lld%lld%d%d",&h,&w,&m,&n); for(int i=0;i<n;++i) a[i].init(); sort(a,a+n); int Mx=(1<<n)-1; for(int i=1;i<=Mx;++i){//暴力处理交集 p.xl=1;p.yl=1;p.xr=h;p.yr=w; for(int j=0,u=i;!p.null()&&u;u>>=1,++j) if(u&1) p&=a[j]; ist[i]=p.null()?0:p.area(); } for(int i=1;i<=Mx;++i)//暴力处理并集 for(int j=i;j;j=(j-1)&i) uni[i]+=((siz[j]&1)?1:-1)*ist[j]; int nw=0,ls=0; ll sum,tot,re,k,ans=Pow(m,h*w-uni[Mx]); for(int i=0;i<n;++i){//分层处理最大值为$a[i].v$的块的方案数 nw|=(1<<i); if(a[i].v==a[i+1].v) continue; sum=uni[nw|ls]-uni[ls]; re=Pow(a[i].v,sum); for(int u=nw;u;u=(u-1)&nw){ tot=uni[u|ls]-uni[ls]; k=Pow(a[i].v-1,tot)*Pow(a[i].v,sum-tot)%mod; if(siz[u]&1) re=(re-k+mod)%mod; else re=(re+k)%mod; }ans=ans*re%mod; ls|=nw; nw=0; }printf("%lld ",ans); } int main(){ for(int i=1;i<=1023;++i) siz[i]=siz[i>>1]+(i&1); scanf("%d",&T); while(T--) Solve(); return 0; }