参考:题解 P3813 【[FJOI2017]矩阵填数】

题目大意:

给定一个 h∗w 的矩阵,矩阵的行编号从上到下依次为 1...h ,列编号从左到右依次 1...w 。

在这个矩阵中你需要在每个格子中填入 1...m 中的某个数。

给这个矩阵填数的时候有一些限制,给定 n个该矩阵的子矩阵,以及该子矩阵的最大值 v ,要求你所填的方案满足该子矩阵的最大值为 v 。

现在,你的任务是求出有多少种填数的方案满足 n 个限制。

两种方案是不一样的当且仅当两个方案至少存在一个格子上有不同的数。由于答案可能很大,你只需要输出答案 mod 1,000,000,007

分析:

首先我们可以发现,处理的时候,应该将所有矩阵按照v的大小升序排列,因为,当两个矩阵范围产生交集的时候,交集处必然要填充小的v。

所以,我们先填v小的,再填v大的,每一个矩阵的面积,都是与之前所有的矩形取并后的面积,再减去之前所有矩形的面积。

也就是,选择之前没有被覆盖的矩形面积。

方案数就是:利用乘法原理,每个矩形处理后,ans加上v的面积次方?

NAIVE!

这些个值,每一个都是所有的方案数,不一定每一个方案数都有一个数能等于最大值v.也就是说可能取不到最大值.

那方案数就是:每个矩形处理后,ans加上:v的面积次方,再减去(v-1)的面积次方?(这就去除不合法解了)

N!A!I!V!E!

我们必须要考虑到这样一种情况:

图中,一共有4个子矩形,其中,三个红色矩形v值相同,假设都为3,黑色矩形v是2,根据计划,刚才已经处理完了。

这个时候,假设我们要处理上面的这个矩形(F所在矩形)会把A+B+F作为剩下的面积进行统计,加入ans。

之后,假设我们要处理中间的矩形(E、G所在矩形)剩下的面积E+C+D+G作为统计。

按照刚才的做法和分析,这些方案都是合法的。

但是我们漏了一种情况:

当A点有v值的时候,因为,上面、中间的矩形要求的最大值相同,所以,A有了v,E、C、D、G区间已经可以瞎选了!!但是这些方案我们没有考虑到。

也就是说,在矩形v相同的时候,且有交集的时候,在交集的地方,会出现一石二鸟,bug。

所以v相同的矩形,必须当做一个大的集合考虑!!

发现,交集这种东西很难考虑。

这个图挺眼熟,像Venn图。

那就用容斥吧。

我们先把所有的可能方案数算出来v^(A+...+H)

有一些方案数,中间矩阵不满足取最大值。减去v^(F+H)*(v-1)^(A+B+C+D+E+G)

一些方案数,上边矩阵不能满足。同理减去。

下边矩阵……

。。。。

但是,中间矩阵,上边矩阵同时不能选(交集)的方案数减了两遍,再加上一遍:交集部分选不了最大值的方案数。

。。。。

以此类推。

具体步骤:

0.输入,排序

1.处理交集

2.处理并集 (利用交集做容斥)

3.按值相同的一组处理

实现其实挺不好写的。状压写的还是少。(蒟蒻)所以参考了题解。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=;
const int M=;
const int mod=1e9+;
ll s[M],u[M];
int siz[M];
int n,m,w,h,t;
ll ans;
inline ll qm(ll x,ll y){
ll ret=,base=x;
while(y){
if(y&) ret=(ret*base)%mod;base=(base*base)%mod;y>>=;
}
return ret;
}//快速幂
struct node{
ll x1,x2,y1,y2;
int v;
inline void rd(){
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&v);
}
inline bool ck(){return (x1>x2)||(y1>y2);}//判断空矩阵
inline ll calc(){return (x2-x1+)*(y2-y1+);}
void operator &=(const node & a)//两个矩阵求交集
{
x1=max(x1,a.x1);y1=max(y1,a.y1);
x2=min(x2,a.x2);y2=min(y2,a.y2);
}
friend bool operator <(node a,node b)
{
return a.v<b.v;
}
}ju[N],tr;//矩阵
void clear()
{
ans=;
memset(s,,sizeof s);
memset(u,,sizeof u);
}
void work()
{
int up=(<<n)-;//全集
for(int i=;i<=up;i++)//交集
{
tr.x1=,tr.y1=,tr.x2=h,tr.y2=w;
bool flag=true;
for(int j=;j<=n;j++)
{
if((<<(j-))&i)
{
tr&=ju[j];
if(tr.ck()){
flag=false;break;
}
}
}
if(flag) s[i]=tr.calc();//s[i],i集合所交集的面积
}
for(int i=;i<=up;i++)//并集
{
ll sum=;
for(int j=i;j;j=(j-)&i)
{
if(siz[j]%==)
{
sum+=s[j];
}
else sum-=s[j];
}//容斥求i集合的并集面积
u[i]=sum;
}
ll ns=,la=;
ll ret=;
for(int i=;i<=n;i++)
{
ns|=(<<(i-));if(ju[i].v==ju[i+].v) continue;//取出所有v相等的矩阵,作为一个集合
ll sheng=u[ns|la]-u[la];//总剩余面积
ret=qm(ju[i].v,sheng);//初始方案数
for(int j=ns;j;j=(j-)&ns)//枚举子集,做容斥
{
ll now=u[j|la]-u[la];
ll kk=(qm(ju[i].v-,now)*qm(ju[i].v,sheng-now))%mod;
if(siz[j]%==)
{
ret=(ret+mod-kk)%mod;
}
else ret=(ret+kk)%mod;
}
la|=ns,ns=;//la,之前所有取过的矩阵集合
ans=ans*ret%mod;
}
ans=ans*qm(m,h*w-u[la])%mod;//别忘了,除子矩阵外的面积也有方案数,可以随便取
printf("%lld\n",ans);
}
int main()
{
scanf("%d",&t);
for(int i=;i<=;i++)
siz[i]=siz[i>>]+(i&);//size[i] i的二进制中1的个数
while(t)
{
scanf("%d%d%d%d",&h,&w,&m,&n);
for(int i=;i<=n;i++) ju[i].rd();
sort(ju+,ju+n+);
ans=;
work();
clear();
t--;
}
return ;
}

[FJOI2017]矩阵填数——容斥的更多相关文章

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

    P3813 [FJOI2017]矩阵填数 shadowice1984说:看到计数想容斥........ 这题中,我们把图分成若干块,每块的最大值域不同 蓝后根据乘法原理把每块的方案数(互不相干)相乘. ...

  2. [BZOJ5010][FJOI2017]矩阵填数(状压DP)

    5010: [Fjoi2017]矩阵填数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 90  Solved: 45[Submit][Status][ ...

  3. bzoj5010: [Fjoi2017]矩阵填数

    Description 给定一个 h*w 的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w.在这个矩阵中你需要在每 个格子中填入 1..m 中的某个数.给这个矩阵填数的时候有一 ...

  4. bzoj 5010: [Fjoi2017]矩阵填数

    Description 给定一个 h*w 的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w.在这个矩阵中你需要在每 个格子中填入 1..m 中的某个数.给这个矩阵填数的时候有一 ...

  5. [luogu P3813] [FJOI2017] 矩阵填数 解题报告 (容斥原理)

    题目链接: https://www.luogu.org/problemnew/show/P3813 题目: 给定一个 h*w的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w. ...

  6. BZOJ5010 FJOI2017矩阵填数(容斥原理)

    如果只考虑某个子矩阵的话,其最大值为v的方案数显然是vsize-(v-1)size.问题在于处理子矩阵间的交叉情况. 如果两个交叉的子矩阵所要求的最大值不同,可以直接把交叉部分划给所要求的最大值较小的 ...

  7. 【BZOJ】5010: [Fjoi2017]矩阵填数

    [算法]离散化+容斥原理 [题意]给定大矩阵,可以每格都可以任意填1~m,给定n个子矩阵,要求满足子矩阵内的最大值为vi,求方案数. n<=10,h,w<=1w. [题解] 此题重点之一在 ...

  8. P3813 [FJOI2017]矩阵填数

    传送门 矩阵很大,但是发现 $n$ 很小,从这边考虑,对于一个一堆小矩阵放在一起的情况 考虑把每一块单独考虑然后方案再乘起来 但是这些奇怪的东西很不好考虑 所以暴力一点,直接拆成一个个小块 但是这样我 ...

  9. [FJOI2017]矩阵填数

    [Luogu3813] [LOJ2280] 写得很好的题解 \(1.\)离散化出每一块内部不互相影响的块 \(2.\)\(dp[i][j]\)为前 \(i\) 种重叠块其中有 \(j\) 这些状态的矩 ...

随机推荐

  1. Linux下DNS简单部署(主从域名服务器)

    一.DNS简介DNS(Domain Name System),域名系统,因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串.通 ...

  2. php安全配置记录和常见错误梳理

    通常部署完php环境后会进行一些安全设置,除了熟悉各种php漏洞外,还可以通过配置php.ini来加固PHP的运行环境,PHP官方也曾经多次修改php.ini的默认设置.下面对php.ini中一些安全 ...

  3. beta阶段性能指标测试

    性能指标概况 安装耗时 启动耗时 CPU占用 内存占用 电池温度 网络流量 平均值 5.48s 1.04s 1.61% 18.68MB 32.44℃ 93.78B 峰值 131.74s 5.13s 5 ...

  4. maven依赖的描述

    maven的坐标和依赖 坐标和依赖,主要涉及的就是pom文件的头部和<dependencies>标签部分(1)pom文件的头部 这里头部不是指pom文件的开头<project> ...

  5. 20135323符运锦----第七周:Linux内核如何装载和启动一个可执行程序

    可执行程序的装载 一.预处理.编译.链接和目标文件的格式 1.可执行程序是怎么得来的 ①编译器预处理 gcc -E -o XX.cpp XX.c (-m32)// 注:把include的文件包含进来, ...

  6. 第三周Linux学习报告

    Linux内核源代码简介: arch/x86中内容重点关注 init目录重要,内核启动相关的代码基本上都在init目录下.如main.c等.Start_kernel函数相当于普通C程序的main函数. ...

  7. “数学口袋精灵”App的第三个Sprint计划----开发日记(第一天12.7~第十天12.16)

    “数学口袋精灵”第三个Sprint计划----第一天 项目进度: 基本完成一个小游戏,游戏具有:随机产生算式,判断对错功能.通过轻快的背景音乐,音效,给玩家提供一个良好的氛围.  任务分配: 冯美欣: ...

  8. java 值传递 数组传递

    在java中,不允许程序员选择值传递还是地址传递各个参数,基本类型总是按值传递.对于对象来说,是将对象的引用也就是副本传递给了方法,在方法中只有对对象进行修改才能影响该对象的值,操作对象的引用时是无法 ...

  9. PAT 1019 数字黑洞

    https://pintia.cn/problem-sets/994805260223102976/problems/994805302786899968 给定任一个各位数字不完全相同的4位正整数,如 ...

  10. Appium学习笔记1_获取到APK安装包的Package以及Activity属性值

    我们设置DesiredCapabilities属性值得时候需要设置"appPackage"和"appActivity",如何获取到这两个值呢? 这两个值不是随便 ...