本文将同步发布于:

题目

题目链接:洛谷 P5987LOJ 3320官网

题意概述

在二维平面直角坐标系上,有一个长度为 \(X\),宽度为 \(Y\) 的地图,注意这个地图的左边界和右边界是连通的,下边界和上边界也是连通的,换言之它是个球形结构。

在这个地图里,有 \(X\times Y\) 个格子以及 \(n\) 个边平行坐标轴的矩形。你只知道每个矩形两个对顶点的坐标,请问在最好情况下,被所有 \(n\) 个矩形都覆盖住的格子数量有多少?

\(1\leq n\leq 5\times 10^5\),\(2\leq X,Y\leq 10^9\)。

题解

几何性质

首先不难发现,\(x,y\) 两维是独立的。

考虑一个矩形的选取情况,只有 \(00,01,10,11\) 四种情况。

然而,两个状态 \(0,1\) 相乘即可得到上面的所有状态,因此我们可以两维分开做。

枚举答案区间

考虑对于一维的情况,一个点对 \((x_1,y_1),(x_2,y_2)\) 仅对应两种可能 \([x_1,x_2]\) 或者 \([0,x_1)\cup(x_2,x]\)。

这两个集合显然不交,因此,我们可以考虑枚举一个区间 \([i,i+1]\),那么可以轻易地确定每个区间的选择方案,因而求出最终的长度。

这个可以离散化后可以用扫描线维护,用线段树维护:

  • 查询操作:区间最大值及个数;
  • 修改操作:区间加法。

时间复杂度为 \(\Theta(n\log_2n)\),可以通过本题。

随机算法有前途

我们想到一个简单的方案,如果区间数量 \(\leq 64\),那么我们对于第 \(i\) 个区间 \([x_1,x_2]\) 内的数,都异或上 \(2^i\),那么操作结束之后,所有异或值相同的区间对应的集合选择方案必然相同,换句话说,它们是一块的。

因此,如果区间数量 \(\leq 64\),我们只需要求出相同异或值的出现次数的最大值即可。

可是这个题目显然不会有上面那么紧的约束,我们考虑放松约束。

具体地,我们不再强求异或的值为 \(2^i\),而是变成一个随机非负整数 \(\in[0,2^{64})\)。统计答案的方法与之前相同,时间复杂度为 \(\Theta(n\log_2n)\),可是正确性呢?

我们的答案出错,是因为有两个不该在同一块的位置经过异或后出现了相同的值,那么我们考虑每一位上异或值相同的概率均为 \(\frac{1}{2}\),那么这个算法正确的概率根据生日悖论为:

\[\prod_{k=0}^{2n-1}(1-\frac{k}{2^{64}})^2
\]

这个数字经过计算可以知道非常接近 \(1\),正确性得到保证。

参考程序

线段树的代码:

#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
static char buf[1<<21],*p1=buf,*p2=buf;
inline int read(void){
reg char ch=getchar();
reg int res=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))res=10*res+(ch^'0'),ch=getchar();
return res;
} inline int max(reg int a,reg int b){
return a>b?a:b;
} inline int min(reg int a,reg int b){
return a<b?a:b;
} const int MAXN=5e5+5; struct Interval{
int l,r;
inline Interval(reg int l=0,reg int r=0):l(min(l,r)),r(max(l,r)){
return;
}
}; struct Event{
int x,id;
inline Event(reg int x=0,reg int id=0):x(x),id(id){
return;
}
inline bool operator<(const Event& a)const{
return x<a.x;
}
}; int n,X,Y;
Interval a[MAXN];
Interval b[MAXN];
vector<int> V; namespace SegmentTree{
#define lson ( (k) << 1 )
#define rson ( (k) << 1 | 1 )
#define mid ( ( (l) + (r) ) >> 1 )
struct Node{
int Max,cnt;
int tAdd;
#define Max(x) unit[(x)].Max
#define cnt(x) unit[(x)].cnt
#define tAdd(x) unit[(x)].tAdd
};
Node unit[MAXN<<3];
inline void pushup(reg int k){
if(Max(lson)>Max(rson))
Max(k)=Max(lson),cnt(k)=cnt(lson);
else if(Max(lson)==Max(rson))
Max(k)=Max(lson),cnt(k)=cnt(lson)+cnt(rson);
else
Max(k)=Max(rson),cnt(k)=cnt(rson);
return;
}
inline void build(reg int k,reg int l,reg int r){
tAdd(k)=0;
if(l==r){
Max(k)=0,cnt(k)=V[l+1]-V[l];
return;
}
build(lson,l,mid),build(rson,mid+1,r);
pushup(k);
return;
}
inline void add(reg int k,reg int val){
Max(k)+=val,tAdd(k)+=val;
return;
}
inline void pushdown(reg int k){
if(tAdd(k)){
add(lson,tAdd(k)),add(rson,tAdd(k));
tAdd(k)=0;
}
return;
}
inline void update(reg int k,reg int l,reg int r,reg int L,reg int R,reg int val){
if(L<=l&&r<=R){
add(k,val);
return;
}
pushdown(k);
if(L<=mid)
update(lson,l,mid,L,R,val);
if(R>mid)
update(rson,mid+1,r,L,R,val);
pushup(k);
return;
}
#undef lson
#undef rson
#undef mid
#undef Max
#undef cnt
#undef tAdd
} inline int solve(reg int n,reg Interval a[],int X){
V.clear();
V.reserve((n+1)<<1);
for(reg int i=1;i<=n;++i){
V.push_back(a[i].l);
V.push_back(a[i].r);
}
V.push_back(0),V.push_back(X);
sort(V.begin(),V.end()),V.erase(unique(V.begin(),V.end()),V.end());
for(reg int i=1;i<=n;++i){
a[i].l=lower_bound(V.begin(),V.end(),a[i].l)-V.begin();
a[i].r=lower_bound(V.begin(),V.end(),a[i].r)-V.begin();
}
X=lower_bound(V.begin(),V.end(),X)-V.begin();
reg int s=V.size()-1;
vector<Event> E;
E.reserve(n<<1);
SegmentTree::build(1,0,s-1);
for(reg int i=1;i<=n;++i){
E.push_back(Event(a[i].l,i));
E.push_back(Event(a[i].r,-i));
SegmentTree::update(1,0,s-1,0,s-1,1);
SegmentTree::update(1,0,s-1,a[i].l,a[i].r-1,-1);
}
sort(E.begin(),E.end());
reg int ptr=0;
reg int res=0;
for(reg int i=0;i<s;++i){
while(ptr<(int)E.size()&&E[ptr].x<=i){
reg int id=abs(E[ptr].id);
if(E[ptr].id>0){
SegmentTree::update(1,0,s-1,0,s-1,-1);
SegmentTree::update(1,0,s-1,a[id].l,a[id].r-1,2);
}
else{
SegmentTree::update(1,0,s-1,0,s-1,1);
SegmentTree::update(1,0,s-1,a[id].l,a[id].r-1,-2);
}
++ptr;
}
res=max(res,SegmentTree::unit[1].cnt);
}
return res;
} int main(void){
n=read(),X=read(),Y=read();
for(reg int i=1;i<=n;++i){
static int x1,y1,x2,y2;
x1=read(),y1=read(),x2=read(),y2=read();
a[i]=Interval(x1,x2);
b[i]=Interval(y1,y2);
}
reg int ansx=solve(n,a,X);
reg int ansy=solve(n,b,Y);
reg ll ans=1ll*ansx*ansy;
printf("%lld\n",ans);
return 0;
}

随机化算法的代码:

#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;
typedef unsigned long long ull;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
static char buf[1<<21],*p1=buf,*p2=buf;
inline int read(void){
reg char ch=getchar();
reg int res=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))res=10*res+(ch^'0'),ch=getchar();
return res;
} inline int max(reg int a,reg int b){
return a>b?a:b;
} inline int min(reg int a,reg int b){
return a<b?a:b;
} const int MAXN=5e5+5; struct Interval{
int l,r;
inline Interval(reg int l=0,reg int r=0):l(min(l,r)),r(max(l,r)){
return;
}
}; int n,X,Y;
Interval a[MAXN];
Interval b[MAXN]; struct Event{
int x;
ull val;
inline Event(reg int x=0,reg ull val=0):x(x),val(val){
return;
}
inline bool operator<(const Event& a)const{
return x<a.x;
}
}; struct Segment{
int len;
ull val;
inline Segment(reg int len=0,reg ull val=0):len(len),val(val){
return;
}
inline bool operator<(const Segment& a)const{
return val<a.val;
}
}; mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count()); inline int solve(reg int n,reg Interval a[],reg int X){
vector<Event> E;
E.reserve((n+1)<<1);
for(reg int i=1;i<=n;++i){
reg ull tag=rng();
E.push_back(Event(a[i].l,tag));
E.push_back(Event(a[i].r,tag));
}
E.push_back(Event(0,0));
E.push_back(Event(X,0));
sort(E.begin(),E.end());
vector<Segment> V;
reg ull val=0;
for(reg int i=0,siz=E.size();i<siz-1;++i){
val^=E[i].val;
if(E[i].x<E[i+1].x)
V.push_back(Segment(E[i+1].x-E[i].x,val));
}
sort(V.begin(),V.end());
reg ull las=0;
reg int sum=0;
reg int res=0;
for(reg int i=0,siz=V.size();i<siz;++i){
if(las==V[i].val)
sum+=V[i].len;
else
las=V[i].val,sum=V[i].len;
res=max(res,sum);
}
return res;
} int main(void){
n=read(),X=read(),Y=read();
for(reg int i=1;i<=n;++i){
static int x1,y1,x2,y2;
x1=read(),y1=read(),x2=read(),y2=read();
a[i]=Interval(x1,x2);
b[i]=Interval(y1,y2);
}
reg int ansx=solve(n,a,X);
reg int ansy=solve(n,b,Y);
reg ll ans=1ll*ansx*ansy;
printf("%lld\n",ans);
return 0;
}

「题解」PA2019 Terytoria的更多相关文章

  1. 「题解」「美团 CodeM 资格赛」跳格子

    目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...

  2. 「题解」「HNOI2013」切糕

    文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...

  3. 「题解」JOIOI 王国

    「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...

  4. 「题解」:[loj2763][JOI2013]现代豪宅

    问题 A: 现代豪宅 时间限制: 1 Sec  内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...

  5. 「题解」:$Six$

    问题 A: Six 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...

  6. 「题解」:$Smooth$

    问题 A: Smooth 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...

  7. 「题解」:Kill

    问题 A: Kill 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 80%算法 赛时并没有想到正解,而是选择了另一种正确性较对的贪心验证. 对于每一个怪,我们定义它的 ...

  8. 「题解」:y

    问题 B: y 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 考虑双向搜索. 定义$cal_{i,j,k}$表示当前已经搜索状态中是否存在长度为i,终点为j,搜索过边 ...

  9. 「题解」:x

    问题 A: x 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 赛时想到了正解并且对拍了很久.对拍没挂,但是评测姬表示我w0了……一脸懵逼. 不难证明,如果对于两个数字 ...

随机推荐

  1. CVE-2012-0158:Microsoft Office MSCOMCTL.ocx 栈溢出漏洞调试分析

    0x01 Lotus Blossom 行动 在 2015 年 6 月,国外安全厂商 Palo Alto Networks 的威胁情报团队 Unit42 发现了一起针对东南亚政府的一次间谍行为,试图获取 ...

  2. Ubuntu Linux DNS服务器 BIND9配置文件命令介绍

    BIND9配置方法 转载▼     配置语法 named.conf acl 定义访问控制列表 controls 定义rndc命令使用的控制通道,若省略,则只允许经过rndc.key认证的127.0.0 ...

  3. Mybatis-Plus03 代码自动生成器

    先看完Mybatis-Plus01和Mybatis-Plus02再看Mybatis-Plus03 AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerato ...

  4. 前端基础问题:CSS vertical-align 属性

    CSS vertical-align 属性与基线的那些事~ 定义和用法: vertical-align 属性设置元素的垂直对齐方式. vertical-align只对内联元素(inline.inlin ...

  5. 2020中国大学生程序设计竞赛(CCPC) - 网络选拔赛总结

    1003 Express Mail Taking 题意:有n个柜子(编号1-n),m封信,k号位置有钥匙,现在需要取信封,并且每取一次信封都要从k号位置进行领取一次钥匙,再去有信封的位置领取信封,问最 ...

  6. 24.Collection集合

    1.Collection集合 1.1数组和集合的区别[理解] 相同点 都是容器,可以存储多个数据 不同点 数组的长度是不可变的,集合的长度是可变的 数组可以存基本数据类型和引用数据类型 集合只能存引用 ...

  7. 封装axios在util中

    创建util工具类,封装通用的get和post请求 封装axios成工具类,方便大家请求调用 1.创建util文件夹 2.创建request.js 3.封装 //封装请求相关方法 //初始化一个axi ...

  8. [设计模式] 设计模式课程(二十)--命令模式(Command)

    概述 "行为变化"模式:组件构建过程中,组件行为的变化经常会导致组件本身剧烈的变化."行为变化"模式将组件的行为和组件本身进行解耦,从而支持组件行为的变化,实现 ...

  9. ruby基础(四)

    ruby基础知识 模块 模块是ruby的特色功能之一.如果说类是事物的实体以及行为,那么模块表现的 就是事物的行为部分,模块和类有以下两点不同: 模块不能拥有实例 模块不能被继承 模块的使用方法 mo ...

  10. python基础之包、模块、命名空间和作用域

    一.模块介绍 模块就是一组功能的集合体,我们的程序可以导入模块来复用模块里的功能. 模块的作用: (1)从文件级别组织程序,更方便管理:随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一 ...