[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]矩阵填数——容斥的更多相关文章
- P3813 [FJOI2017]矩阵填数(组合数学)
P3813 [FJOI2017]矩阵填数 shadowice1984说:看到计数想容斥........ 这题中,我们把图分成若干块,每块的最大值域不同 蓝后根据乘法原理把每块的方案数(互不相干)相乘. ...
- [BZOJ5010][FJOI2017]矩阵填数(状压DP)
5010: [Fjoi2017]矩阵填数 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 90 Solved: 45[Submit][Status][ ...
- bzoj5010: [Fjoi2017]矩阵填数
Description 给定一个 h*w 的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w.在这个矩阵中你需要在每 个格子中填入 1..m 中的某个数.给这个矩阵填数的时候有一 ...
- bzoj 5010: [Fjoi2017]矩阵填数
Description 给定一个 h*w 的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w.在这个矩阵中你需要在每 个格子中填入 1..m 中的某个数.给这个矩阵填数的时候有一 ...
- [luogu P3813] [FJOI2017] 矩阵填数 解题报告 (容斥原理)
题目链接: https://www.luogu.org/problemnew/show/P3813 题目: 给定一个 h*w的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w. ...
- BZOJ5010 FJOI2017矩阵填数(容斥原理)
如果只考虑某个子矩阵的话,其最大值为v的方案数显然是vsize-(v-1)size.问题在于处理子矩阵间的交叉情况. 如果两个交叉的子矩阵所要求的最大值不同,可以直接把交叉部分划给所要求的最大值较小的 ...
- 【BZOJ】5010: [Fjoi2017]矩阵填数
[算法]离散化+容斥原理 [题意]给定大矩阵,可以每格都可以任意填1~m,给定n个子矩阵,要求满足子矩阵内的最大值为vi,求方案数. n<=10,h,w<=1w. [题解] 此题重点之一在 ...
- P3813 [FJOI2017]矩阵填数
传送门 矩阵很大,但是发现 $n$ 很小,从这边考虑,对于一个一堆小矩阵放在一起的情况 考虑把每一块单独考虑然后方案再乘起来 但是这些奇怪的东西很不好考虑 所以暴力一点,直接拆成一个个小块 但是这样我 ...
- [FJOI2017]矩阵填数
[Luogu3813] [LOJ2280] 写得很好的题解 \(1.\)离散化出每一块内部不互相影响的块 \(2.\)\(dp[i][j]\)为前 \(i\) 种重叠块其中有 \(j\) 这些状态的矩 ...
随机推荐
- 【持续更新中···】Linux下的小技巧
1.Linux回到上级文件的命令: cd ..回到上一级目录(注意:cd 和..中间有空格) cd ~回到home目录 cd -回到某一目录
- C#编写WINNT服务,随便解决安卓开发遇到的5037被众多程序无节操占用的问题
需求分析: 最近重新开始学习安卓开发,好久不用的ADT集成开发环境频繁遇到不能在仿真机和真机上调试的问题,也就是本人另一篇博文描述的ADB(Android Debug Bridge)监控的5037被金 ...
- 雅思听听app
最近本人呢,正在紧张的备战雅思考试,因为英语基础很弱,尤其是听力,所以老师推荐了雅思听听这个app,说是特别好使,用了一个多月的,总体来说感觉还是很nice的,但是还有一些小毛病,不过这小毛病瑕不掩瑜 ...
- 【Beta版本发布】爬虫队长装备全面更新!
一.Beta阶段目标回顾 1.为了解决Alpha阶段线程异常泛滥的问题,我们需要一个线程池. 2.为了爬取得到的文件正确可用,我们需要一个异常清理器. 3.为了不间断爬取,管理员不必频繁运行程序点,我 ...
- 《Linux内核设计与实现》第八周读书笔记——第四章 进程调度
<Linux内核设计与实现>第八周读书笔记——第四章 进程调度 第4章 进程调度35 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间,进程调度程序可看做在可运行态进程之间分配 ...
- java — 静态绑定和动态绑定
绑定:一个方法的调用与方法所在的类关联起来.java中的绑定分为静态绑定和动态绑定,又被称作前期绑定和后期绑定. 静态绑定:(final.static.private)在程序执行前已经被绑定,也就是说 ...
- QT下opencv的编译和使用
需要的文件 qt-opensource-windows-x86-mingw491_opengl-5.4.0.exe cmake-3.12.0-rc1-win64-x64.msi opencv-2.4. ...
- PAT L2-002 链表去重
https://pintia.cn/problem-sets/994805046380707840/problems/994805072641245184 给定一个带整数键值的链表 L,你需要把其中绝 ...
- HDU 2033 人见人爱A+B
http://acm.hdu.edu.cn/showproblem.php?pid=2033 Problem Description HDOJ上面已经有10来道A+B的题目了,相信这些题目曾经是大家的 ...
- XShell+Xmanager实现在XShell中显示远程服务器的图形界面
http://blog.csdn.net/linghao00/article/details/8768435