参考:题解 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. Python从菜鸟到高手(3):声明变量

    变量(variable)是Python语言中一个非常重要的概念.变量的主要作用就是为Python程序中的某个值起一个名字.类似于"张三"."李四"." ...

  2. SQL多表查询总结

    前言 连接查询包括合并.内连接.外连接和交叉连接,如果涉及多表查询,了解这些连接的特点很重要.只有真正了解它们之间的区别,才能正确使用. 一.Union UNION 操作符用于合并两个或多个 SELE ...

  3. Node 系列之url模块

    引入 url: const url = require("url"); 用于URL解析.处理等操作的解决方案 1.url.parse(urlStr[, parseQueryStri ...

  4. spring中通过JNDI、DBCP、C3P0配置数据源

    JNDI配置数据源 1.首先在tomcat的server.xml中配置数据源信息,找到Context,然后在里边加入如下代码 <Context docBase="SpringDemo& ...

  5. Alpha 答辩总结模板

    Alpha 答辩总结模板 每个小组提供一篇总结博客(组内共享,每个人都发布),包含: 本组(组名)所有成员(短学号,名,标注组长)(1分) 组内各成员贡献比例,如不提供,取平均分后组长得分减50% G ...

  6. 用delete和trancate删除表记录的区别

    首先说相同点,就是他们都能删除表中的数据,区别有两点: 第一点: delete语句在删除记录的时候可以有选择的删除某些数据(使用where子句),当然,如果不添加where子句,就是删除所有记录 而t ...

  7. Trouble shooting(问题解决):centos 7 gnome show someting has gone wrong.

    centos 7 升级 内核 3.10,startx启动不了了.进界面也是oh,no!someting has gone wrong . 参见帖子:http://bbs.csdn.net/topics ...

  8. mongoDB的配置和使用

    如何启动mongodb? mongod --dbpath C:\appStore\mongodata //数据库地址 再开一个cmder窗口 进入C:\Program Files\MongoDB\Se ...

  9. Node 连接池pool

    //1:加载相应的模块 http url fs mysqlconst http = require("http");const url = require("url&qu ...

  10. OneZero第七周第一次站立会议(2016.5.9)

    1. 时间: 12:15--12:25  共计10分钟. 2. 成员: X 夏一鸣 * 组长 (博客:http://www.cnblogs.com/xiaym896/), G 郭又铭 (博客:http ...