BZOJ5010 FJOI2017矩阵填数(容斥原理)
如果只考虑某个子矩阵的话,其最大值为v的方案数显然是vsize-(v-1)size。问题在于处理子矩阵间的交叉情况。
如果两个交叉的子矩阵所要求的最大值不同,可以直接把交叉部分划给所要求的最大值较小的子矩阵。那么,所要求最大值不同的格子彼此间是独立的。于是现在可以只考虑要求相同的格子。
直接计算似乎很麻烦。由于n很小,考虑一个很套路的容斥:至少0个不满足限制的方案数-至少1个不满足限制的方案数+至少2个不满足限制的方案数……于是我们可以枚举哪些矩阵不满足限制,剩下的随便填(当然要在所限制的最大值之内)。
计算这些矩形的交和并也是一个有点麻烦的问题。可以离散化后暴力统计。这里离散化后应该每个位置表示一段区间比较方便,所以读入时++x2,++y2。由于数据范围实在太小怎么做都行。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define P 1000000007
#define N 13
#define y1 y3
#define y2 y4
int T,r,c,n,m,row[N<<],line[N<<],flag[N<<][N<<],ans,sum,nw,nv;
bool choose[N];
struct data
{
int x1,y1,x2,y2,v,size;
int tag[N<<][N<<];
bool operator <(const data&a) const
{
return v>a.v;
}
}a[N];
int ksm(int a,int k)
{
if (k==) return ;
int tmp=ksm(a,k>>);
if (k&) return 1ll*tmp*tmp%P*a%P;
else return 1ll*tmp*tmp%P;
}
int calc(int v)
{
memset(flag,,sizeof(flag));
for (int i=;i<=n;i++)
if (a[i].v==v&&!choose[i])
for (int j=a[i].x1;j<a[i].x2;j++)
for (int k=a[i].y1;k<a[i].y2;k++)
if (a[i].tag[j][k]) flag[j][k]=;
for (int i=;i<=n;i++)
if (choose[i])
for (int j=a[i].x1;j<a[i].x2;j++)
for (int k=a[i].y1;k<a[i].y2;k++)
if (a[i].tag[j][k]) flag[j][k]=-;
int s1=,s2=;
for (int i=;i<nw;i++)
for (int j=;j<nv;j++)
if (flag[i][j]==) s1+=(row[i+]-row[i])*(line[j+]-line[j]);
else if (flag[i][j]==-) s2+=(row[i+]-row[i])*(line[j+]-line[j]);
return 1ll*ksm(v,s1)*ksm(v-,s2)%P;
}
void dfs(int k,int s,int v)
{
if (k>n)
{
if (s&) sum=(sum-calc(v)+P)%P;
else sum=(sum+calc(v))%P;
return;
}
if (a[k].v==v) choose[k]=,dfs(k+,s+,v);
choose[k]=;dfs(k+,s,v);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj5010.in","r",stdin);
freopen("bzoj5010.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
T=read();
while (T--)
{
r=read(),c=read(),m=read(),n=read();
for (int i=;i<=n;i++)
a[i].x1=read(),a[i].y1=read(),a[i].x2=read()+,a[i].y2=read()+,a[i].v=read(),a[i].size=,memset(a[i].tag,,sizeof(a[i].tag));
int w=,v=;
for (int i=;i<=n;i++)
row[++w]=a[i].x1,row[++w]=a[i].x2,line[++v]=a[i].y1,line[++v]=a[i].y2;
row[++w]=,row[++w]=r+;line[++v]=,line[++v]=c+;
sort(row+,row+w+);sort(line+,line+v+);
nw=unique(row,row+w+)-row-,nv=unique(line,line+v+)-line-;
for (int i=;i<=n;i++)
a[i].x1=lower_bound(row+,row+nw+,a[i].x1)-row,a[i].x2=lower_bound(row+,row+nw+,a[i].x2)-row,
a[i].y1=lower_bound(line+,line+nv+,a[i].y1)-line,a[i].y2=lower_bound(line+,line+nv+,a[i].y2)-line;
sort(a+,a+n+);
memset(flag,,sizeof(flag));
for (int i=;i<=n;i++)
for (int j=a[i].x1;j<a[i].x2;j++)
for (int k=a[i].y1;k<a[i].y2;k++)
flag[j][k]=a[i].v;
for (int i=;i<=n;i++)
for (int j=;j<nw;j++)
for (int k=;k<nv;k++)
if (flag[j][k]==a[i].v) a[i].size++,a[i].tag[j][k]=;
ans=;
for (int i=;i<nw;i++)
for (int j=;j<nv;j++)
if (flag[i][j]==) ans=1ll*ans*ksm(m,(row[i+]-row[i])*(line[j+]-line[j]))%P;
for (int i=;i<=n;i++)
{
sum=;int t=i;
while (a[t+].v==a[i].v) t++;
memset(choose,,sizeof(choose));
dfs(i,,a[i].v);
ans=1ll*ans*sum%P;
i=t;
}
cout<<ans<<endl;
}
return ;
}
BZOJ5010 FJOI2017矩阵填数(容斥原理)的更多相关文章
- [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 中的某个数.给这个矩阵填数的时候有一 ...
- P3813 [FJOI2017]矩阵填数(组合数学)
P3813 [FJOI2017]矩阵填数 shadowice1984说:看到计数想容斥........ 这题中,我们把图分成若干块,每块的最大值域不同 蓝后根据乘法原理把每块的方案数(互不相干)相乘. ...
- [FJOI2017]矩阵填数——容斥
参考:题解 P3813 [[FJOI2017]矩阵填数] 题目大意: 给定一个 h∗w 的矩阵,矩阵的行编号从上到下依次为 1...h ,列编号从左到右依次 1...w . 在这个矩阵中你需要在每个格 ...
- [luogu P3813] [FJOI2017] 矩阵填数 解题报告 (容斥原理)
题目链接: https://www.luogu.org/problemnew/show/P3813 题目: 给定一个 h*w的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w. ...
- bzoj 5010: [Fjoi2017]矩阵填数
Description 给定一个 h*w 的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w.在这个矩阵中你需要在每 个格子中填入 1..m 中的某个数.给这个矩阵填数的时候有一 ...
- 【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\) 这些状态的矩 ...
随机推荐
- C++的 new 和 detele
什么都不说 直接上代码 哈哈 #include <iostream> using namespace std; int main(int argc, char *argv[]) { co ...
- (转)60s快速分析Linux性能
之前在地铁上看到过一篇快速分析Linux系统性能的文章,觉得以后有用,今天就找了一下,转载过来. 原文出处:http://techblog.netflix.com/2015/11/linux-perf ...
- 浅谈Java泛型中的extends和super关键字
泛型是在Java 1.5中被加入了,这里不讨论泛型的细节问题,这个在Thinking in Java第四版中讲的非常清楚,这里要讲的是super和extends关键字,以及在使用这两个关键字的时候为什 ...
- SQL Server 索引中include的魅力(具有包含性列的索引)(转载)
开文之前首先要讲讲几个概念 [覆盖查询] 当索引包含查询引用的所有列时,它通常称为“覆盖查询”. [索引覆盖] 如果返回的数据列就包含于索引的键值中,或者包含于索引的键值+聚集索引的键值中,那么就不 ...
- CF835F Roads in the Kingdom/UOJ126 NOI2013 快餐店 树的直径
传送门--CF 传送门--UOJ 题目要求基环树删掉环上的一条边得到的树的直径的最小值. 如果直接考虑删哪条边最优似乎不太可做,于是考虑另一种想法:枚举删掉的边并快速地求出当前的直径. 对于环上的点, ...
- (一)ABP添加控制器和页面(有时候页面不出来)
1:添加控制器后需要写[Area("AppAreaName")] 2:继承 WebControllerBase 3:创建视图就可以出现index页面了
- [HNOI2018]游戏[拓扑排序]
题意 题目链接 分析 先将没有锁的房间缩点,首先有一个 \(O(n^2)\) 的想法:从每个点出发,每次检查能否向两边扩张. 容易发现门和门之间如果有锁,必然只有一方能够开锁(只有一把钥匙),并且能够 ...
- 「Fluent Python」今年最佳技术书籍
Fluent Python 读书手记 Python数据模型:特殊方法用来给整个语言模型特殊使用,一致性体现.如:__len__, __getitem__ AOP: zope.inteface 列表推导 ...
- 《坦克世界》1.0+:使用 CPU 优化的图形和物理丰富用户体验
本文以<坦克世界>为例,介绍 Wargaming 使用 CPU 多核和 CPU 单指令多数据 (SIMD) 功能显著提升游戏沉浸式体验的创新方法.我们以英特尔® 线程构建模块(英特尔® T ...
- Jumpserver双机高可用环境部署笔记
之前在IDC部署了Jumpserver堡垒机环境,作为登陆线上服务器的统一入口.后面运行一段时间后,发现Jumpserver服务器的CPU负载使用率高达80%以上,主要是python程序对CPU的消耗 ...