• 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;
    }
  • 相关阅读:
    vue之父子组件间通信实例讲解(props、$ref、$emit)
    jsp的内置对象
    一道关于类加载顺序的面试题
    web.xml中的load-on-startup
    静态代理、JDK动态代理和CGLib动态代理之前的区别
    有关于tomcat启动时,利用listener来执行某个方法
    有关于注解
    java 代码块,静态代码块,构造器等的执行顺序
    java容器的理解(collection)
    PHP常用的缓存技术汇总
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/10631980.html
Copyright © 2020-2023  润新知