本文将同步发布于:

题目

题目链接:洛谷 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. Linux下抓包命令tcpdump的使用

    在linux下,可以使用 tcpdump 命令来抓取数据包. 主要用法如下: 过滤网卡 tcpdump -i eth0 #抓取所有经过网卡eth0数据包 tcpdump -i lo #抓取环回口的数据 ...

  2. Day003 包机制

    包机制 为了更好地组织类,Java提供了包机制,用于区别类名的命名空间. 包语句的语法格式为: package pkg1[.pkg2[.pkg3....]]; 一般利用公司的域名倒置作为包名; 例如: ...

  3. linux命令的使用 以及基本docker命令及docker镜像安装

    以linux CentOS-7 64位 系统为例 查看ip  ifconfig 固定ip 输入vim /etc/sysconfig/network-scripts/ifcfg-ens3 其中vim是修 ...

  4. C++ primer plus读书笔记——第11章 使用类

    第11章 使用类 1. 运算符重载是一种形式的C++多态. 2. 不要返回指向局部变量或临时对象的引用.函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据. 3. 运算符重载的格式如下: ...

  5. 【敏杰开发】Beta阶段项目展示

    [敏杰开发]Beta阶段项目展示 项目相关地址汇总 线上地址:http://roadmap.imcoming.top 前端仓库:https://github.com/MinJieDev/Roadmap ...

  6. ESLint语法报错问题

    编写javaScript过程中ESLint语法报错问题 ESLint语法要求: 双引号""需要替换成单引号'' 分号不允许出现 ()之前需要一个空格比如 login () (VSC ...

  7. 用户添加到sudoer列表## Allow root to run any commands anywhere root ALL=(ALL) ALL Iron ALL=(ALL) ALL

    将用户添加到sudoer列表 李序锴关注 2017.12.20 15:03:25字数 605阅读 4,067 默认情况下,linux没有将当前用户列入到sudoer列表中(在redhat系列的linu ...

  8. [刷题] 75 Sort Colors

    要求 给只有0 1 2三个元素的数组排序 思路 方法1:遍历数组,利用辅助数组保存三个元素的个数,再写入(遍历两遍) 辅助数组有三个元素,对应0 1 2的个数 方法2:模拟三路快排,遍历一遍完成排序 ...

  9. 安装centos7提示 please make your choice from above

    分别输入"1" "2" "q" "yes",如上图 释义如下:

  10. cat ~/.bash_history

    7.history命令 history命令用于显示历史执行过的命令,格式为"history [-c]". history命令应该是作者最喜欢的命令.执行history命令能显示出当 ...