码迷,mamicode.com
首页 > 其他好文 > 详细

FJOI2017 矩阵填数

时间:2020-02-20 10:01:11      阅读:65      评论:0      收藏:0      [点我收藏+]

标签:code   cup   矩形   const   www   面积   cap   cpp   turn   

题目

给定一个 \(h\times w\) 的矩阵,每个格子中将填入 \(1\)\(m\) 中的某个整数。
一个合法的填数方案须满足 \(n\) 条限制,每条限制形如“以 \((x_1,y_1)\) 为左上角,\((x_2,y_2)\) 为右下角的子矩阵中,最大值必须为 \(v\)”。
求填数方案数,对大质数取模。

把这 \(n\) 条限制按照 \(v\) 从小到大排序。

这样,就可以对每个 \(x\) 求出最小限制为 \(x\) 的区域的答案,最后处理没被限制的区域(这显然是 \(m^{S_R}\)\(S_R\) 没被限制的区域的面积)。

答案就是把这些东西全部乘起来。

接下来考虑对于每个 \(x\) 求出最小限制为 \(x\) 的区域的答案。

\(A\) 为所有 \(v\) 值等于 \(x\) 的限制构成的集合。我们要满足 \(A\) 中所有限制,并不好算,考虑容斥。

改为对 \(A\) 的每个子集 \(B\),求强制让其不满足的方案数,大概是 \((x-1)^{S_B}x^{S_{A-B}}\) ,其中 \(S_A\) 为集合 \(A\) 中所有子矩阵的并的面积去掉其中有更小限制的面积。然后加加减减就行。

发现我们还要求区域面积,当然可以离散化坐标值然后随便搞,但那个细节巨多。考虑更好写的方法:继续容斥。

我们发现本题中所有的面积都可以转为矩形的交和并。

发现矩形交是好求的,而并的就等于其子集的交加加减减,然后就求完了。枚举子集的子集是 \(O(3^n)\) 的。

每组数据的复杂度为 \(O(3^n+2^nn\log(hw))\)\(\log\) 来自快速幂。(当然可以 FMT 把 \(3^n\) 优化掉,优化到 \(O(2^nn\log(hw))\),但没有必要)。

#include<cstdio>
#include<algorithm>
typedef long long ll;
const int N=10,M=1e9+7;
inline int Pow(int a,int m){int s=1;for(;m;m>>=1)m&1?s=(ll)s*a%M:0,a=(ll)a*a%M;return s;}
int x[N],y[N],xx[N],yy[N],mx[N],t[N],n,m,h,w,cnt[1<<N],ans,tmp;ll cup[1<<N],cap[1<<N];
bool Cmp(const int&i,const int&j){return mx[i]<mx[j];}
int main(){
    int a,b,aa,bb,U;
    for(int I=0;I<(1<<N);I++)cnt[I]=cnt[I>>1]+(I&1);
    int T;scanf("%d",&T);for(;T--;){
    scanf("%d%d%d%d",&h,&w,&m,&n);
    for(int i=0;i<n;i++)scanf("%d%d%d%d%d",x+i,y+i,xx+i,yy+i,mx+i),t[i]=i;
    std::sort(t,t+n,Cmp);
    for(int I=0;I<(1<<n);I++){
      a=b=0,aa=h,bb=w;
      for(int i=0;i<n;i++)if(I&1<<i){
        a=std::max(a,x[i]);
        b=std::max(b,y[i]);
        aa=std::min(aa,xx[i]);
        bb=std::min(bb,yy[i]);
      }
      cap[I]=a>aa||b>bb?0:(ll)(aa-a+1)*(bb-b+1);
    }
    for(int I=0;I<(1<<n);I++){
      cup[I]=0;
      for(int J=I;J;J=I&J-1)
        cup[I]=(cup[I]+cap[J]*(cnt[J]&1?1:-1));
    }
    ans=Pow(m,h*w-cup[(1<<n)-1]);
    U=0;
    for(int l=0,r=0,I;r<n;r++)if(r+1==n||mx[t[r]]!=mx[t[r+1]]){
      I=0;
      for(int i=l;i<=r;i++)I|=1<<t[i];
      tmp=Pow(mx[t[r]],cup[I|U]-cup[U]);
      for(int J=I;J;J=I&J-1)
        tmp=(tmp+(ll)Pow(mx[t[r]]-1,cup[J|U]-cup[U])*Pow(mx[t[r]],cup[I|U]-cup[J|U])%M*(cnt[J]&1?-1:1)+M)%M;
      ans=(ll)ans*tmp%M;
      U|=I,l=r+1;
    }
    printf("%d\n",ans);
    }return 0;
}

FJOI2017 矩阵填数

标签:code   cup   矩形   const   www   面积   cap   cpp   turn   

原文地址:https://www.cnblogs.com/Camp-Nou/p/12334197.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!