P3813 [FJOI2017]矩阵填数(组合数学)

P3813 [FJOI2017]矩阵填数

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;
}
原文地址:https://www.cnblogs.com/kafuuchino/p/10631980.html