• bzoj5010: [Fjoi2017]矩阵填数


    Description

    给定一个 h*w 的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w。在这个矩阵中你需要在每
    个格子中填入 1..m 中的某个数。给这个矩阵填数的时候有一些限制,给定 n 个该矩阵的子矩阵,以及该子矩阵的
    最大值 v,要求你所填的方案满足该子矩阵的最大值为 v。现在,你的任务是求出有多少种填数的方案满足 n 个限
    制。两种方案是不一样的当且仅当两个方案至少存在一个格子上有不同的数。由于答案可能很大,你只需要输出答
    案 对 1,000,000,007 的取模即可。

    Input

    输入数据的第一行为一个数 T,表示数据组数。
    对于每组数据,第一行为四个数 h,w,m,n。
    接下来 n 行,每一行描述一个子矩阵的最大值 v。每行为五个整
    数 x1,y1,x2,y2,v,表示一个左上角为(x1,y1),右下角为(x2,y2)的子矩阵的最大
    值为 v ( 1≤x1≤x2≤h, 1≤y1≤y2≤w)
    T≤5,1≤h,w,m≤10000,1≤v≤m,1≤n≤10

    Output

    对于每组数据输出一行,表示填数方案 mod 1,000,000,007 后的值。
     
    对限制条件容斥,转为每个子矩阵内不超过某个数,由于不同的坐标很少,对坐标离散化后可以O(n^2)暴力统计。
    #include<bits/stdc++.h>
    typedef long long i64;
    const int P=1e9+7;
    int T,h,w,m,n;
    int xs[33],ys[33],xp,yp,vs[33],vp,ts[33];
    int rc[33][5],mv[33][33],as[33][33];
    void mins(int&a,int b){if(a>b)a=b;}
    int pw(int a,int n){
        int v=1;
        for(;n;n>>=1,a=i64(a)*a%P)if(n&1)v=i64(v)*a%P;
        return v;
    }
    int main(){
        for(scanf("%d",&T);T;--T){
            int ans=0;
            scanf("%d%d%d%d",&h,&w,&m,&n);
            xp=yp=vp=0;
            xs[xp++]=1;
            xs[xp++]=h+1;
            ys[yp++]=1;
            ys[yp++]=w+1;
            vs[vp++]=m;
            for(int i=0;i<n;++i){
                for(int j=0;j<5;++j)scanf("%d",rc[i]+j);
                xs[xp++]=rc[i][0];
                xs[xp++]=rc[i][2]+1;
                ys[yp++]=rc[i][1];
                ys[yp++]=rc[i][3]+1;
                vs[vp++]=rc[i][4];
                vs[vp++]=rc[i][4]-1;
            }
            std::sort(xs,xs+xp);
            xp=std::unique(xs,xs+xp)-xs-1;
            std::sort(ys,ys+yp);
            yp=std::unique(ys,ys+yp)-ys-1;
            std::sort(vs,vs+vp);
            vp=std::unique(vs,vs+vp)-vs;
            for(int i=0;i<xp;++i)
            for(int j=0;j<yp;++j)as[i][j]=(xs[i+1]-xs[i])*(ys[j+1]-ys[j]);
            for(int t=0;t<n;++t){
                rc[t][0]=std::lower_bound(xs,xs+xp,rc[t][0])-xs;
                rc[t][2]=std::lower_bound(xs,xs+xp,rc[t][2]+1)-xs;
                rc[t][1]=std::lower_bound(ys,ys+yp,rc[t][1])-ys;
                rc[t][3]=std::lower_bound(ys,ys+yp,rc[t][3]+1)-ys;
                rc[t][4]=std::lower_bound(vs,vs+vp,rc[t][4])-vs;
            }
            for(int S=0;S<(1<<n);++S){
                for(int i=0;i<xp;++i)
                for(int j=0;j<yp;++j)mv[i][j]=vp-1;
                int s=1;
                for(int t=0;t<n;++t){
                    int v=rc[t][4];
                    if(S>>t&1)s=-s,--v;
                    for(int i=rc[t][0];i<rc[t][2];++i)
                    for(int j=rc[t][1];j<rc[t][3];++j)mins(mv[i][j],v);
                }
                for(int i=0;i<vp;++i)ts[i]=0;
                for(int i=0;i<xp;++i)
                for(int j=0;j<yp;++j)ts[mv[i][j]]+=as[i][j];
                for(int i=0;i<vp;++i)s=i64(s)*pw(vs[i],ts[i])%P;
                ans=(ans+s)%P;
            }
            printf("%d
    ",(ans+P)%P);
        }
        return 0;
    }
  • 相关阅读:
    纪念这一天,我找到了实习工作
    在编程的道路上坚定地走下去
    今天是1024程序员节
    趣谈函数调用与返回值
    为期3个月的实训结束了,有感而发
    学习编程时遇到难点怎么办?
    今天学习了安卓中的UI线程
    Java程序员要掌握的常用快捷键
    我是一个注重基础的人
    我也有自己的小家啦
  • 原文地址:https://www.cnblogs.com/ccz181078/p/7501611.html
Copyright © 2020-2023  润新知