「题解」PA2019 Terytoria
本文将同步发布于:
题目
题意概述
在二维平面直角坐标系上,有一个长度为 \(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}\),那么这个算法正确的概率根据生日悖论为:
\]
这个数字经过计算可以知道非常接近 \(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的更多相关文章
- 「题解」「美团 CodeM 资格赛」跳格子
目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...
- 「题解」「HNOI2013」切糕
文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...
- 「题解」JOIOI 王国
「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...
- 「题解」:[loj2763][JOI2013]现代豪宅
问题 A: 现代豪宅 时间限制: 1 Sec 内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...
- 「题解」:$Six$
问题 A: Six 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...
- 「题解」:$Smooth$
问题 A: Smooth 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...
- 「题解」:Kill
问题 A: Kill 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 80%算法 赛时并没有想到正解,而是选择了另一种正确性较对的贪心验证. 对于每一个怪,我们定义它的 ...
- 「题解」:y
问题 B: y 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 考虑双向搜索. 定义$cal_{i,j,k}$表示当前已经搜索状态中是否存在长度为i,终点为j,搜索过边 ...
- 「题解」:x
问题 A: x 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 赛时想到了正解并且对拍了很久.对拍没挂,但是评测姬表示我w0了……一脸懵逼. 不难证明,如果对于两个数字 ...
随机推荐
- [LeetCode每日一题]781. 森林中的兔子
[LeetCode每日一题]781. 森林中的兔子 问题 森林中,每个兔子都有颜色.其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色.我们将这些回答放在 answers 数组里. ...
- 基于linux信号的timeout装饰器
在做基于ray的分布式任务处理时,偶尔遇到由于ray集群不稳定导致的长时间连接不上,进而导致程序卡死,无法向后端返回任务状态的情况.但是ray的初始化函数本身未实现超时机制,因此设计基于多线程+信号的 ...
- Spring MVC工作原理及源码解析(三) HandlerMapping和HandlerAdapter实现原理及源码解析
1.HandlerMapping实现原理及源码解析 在前面讲解Spring MVC工作流程的时候我们说过,前端控制器收到请求后会调⽤处理器映射器(HandlerMapping),处理器映射器根据请求U ...
- 改善c++程序的150个建议(读后总结)-------0-9
0. 不要让main 函数返回 void 入口函数main()返回类型应该为 int, 即程序结束时return 0 表示程序正常返回,函数结束时 return -1 值表示程序异常返回, 如果不显式 ...
- QFNU-11.08training
7-1 阅览室 题目: 天梯图书阅览室请你编写一个简单的图书借阅统计程序.当读者借书时,管理员输入书号并按下S键,程序开始计时:当读者还书时,管理员输入书号并按下E键,程序结束计时.书号为不超过10 ...
- Ubuntu执行命令时,不sudo提示权限不足,sudo提示找不到该命令
问题:Ubuntu执行命令时,不sudo提示权限不足,sudo提示找不到该命令 补充描述:尝试将命令所在路径添加到/etc/profile中(所有用户环境变量),结果sudo -i切换到root用户后 ...
- 大量客户名片如何轻松导入到CRM系统里?
当您组织或参与了一次线下活动或展会,肯定会收集到非常多的潜在客户的名片.这个时候您是不是在发愁如何将这些信息导入到CRM系统中? 可以想到,您肯定会将这些名片分发给销售人员,让他们手动录入--这也确实 ...
- copy和deep.copy
https://blog.csdn.net/qq_32907349/article/details/52190796 加上crossin公众号上的可变对象与不可变对象 a=[1,2,3,[4]] b= ...
- 3D饼/环Echarts图的实现
首先确保在项目中引入了echarts和echarts-gl"echarts": "^4.9.0","echarts-gl": "^ ...
- [bug] Hive:Caused by: MetaException(message:Hive Schema version 2.1.0 does not match metastore's schema version 1.2.0 Metastore is not upgraded or corrupt)
参考 https://www.cnblogs.com/liupuLearning/p/6610307.html 少了创建hive数据库一步