题目:https://loj.ac/problem/2585

算答案的时候要二分!

这样的话,就是对于询问位置 x ,二分出一个最小的 mid 使得 [ x-mid , x+mid ] 里包含所有种类的商店。

判断一个区间里包含所有种类商店的方法是对于每种商店,记录每个这种商店的同类型前驱;然后看看 [ x+mid+1 , INF ] 里所有种类商店的前驱最小值是不是 < x+mid 就行了。

  实现方法就是对于每个种类开一个 set 维护该种类商店的所有位置,再对所有种类开一个线段树维护这个区间里的各种商店的前驱最小值;

  新增一个 x 位置的商店,就改一下线段树 x 位置的值,再改一下 x 的同类型后继位置的线段树的值。删除一个商店也类似。

  因为可能同一时刻同种商店在同一个位置,所以用 multiset ;因为同一时刻不同商店可能在同一个位置,导致同一个位置有不同的前驱值,所以线段树每个叶子开 可删堆 或 set 维护该位置所有前驱,取其中的最小值作为线段树该点的值。

对于无解的情况要特判。可以用一个 可删堆 记录所有种类的最靠前的商店位置,只要在加入或删除商店的时候看看它是不是 set 里最靠前元素即可。

这样是 nlog2n 的。自己实现得很不好,TLE。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#define pb push_back
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
int Mn(int a,int b){return a<b?a:b;}
int Mx(int a,int b){return a>b?a:b;}
const int N=3e5+,M=*N,M2=N<<;//M:time M2:pos
int n,k,Q,R,tp[M],tot,ls[M<<],rs[M<<];
int p0,ans[N];
struct Sp{int x,t,a,b;}t[N];
struct Node{
int x,t,id;
Node(int x=,int t=):x(x),t(t) {}
bool operator< (const Node &b)const
{return t<b.t;}
}qr[N];
vector<Node> vt[M<<];
struct Tr{
int R,ls[M2<<],rs[M2<<],mn[M2<<];
multiset<int> st[N];
multiset<int>::iterator it,it2;
priority_queue<int,vector<int>,greater<int> > q[M2],dq[M2];
priority_queue<int> q2,dq2;
void build(int l,int r,int cr)
{
mn[cr]=R; if(l==r)return; int mid=l+r>>;
ls[cr]=++tot; build(l,mid,ls[cr]);
rs[cr]=++tot; build(mid+,r,rs[cr]);
}
void init()
{
for(int i=;i<=n;i++) tp[++R]=t[i].x;
for(int i=;i<=Q;i++) tp[++R]=qr[i].x;
sort(tp+,tp+R+); R=unique(tp+,tp+R+)-tp-;
for(int i=;i<=n;i++) t[i].x=lower_bound(tp+,tp+R+,t[i].x)-tp;
for(int i=;i<=Q;i++) qr[i].x=lower_bound(tp+,tp+R+,qr[i].x)-tp;
R++; tot=; build(,R,);
for(int i=;i<=k;i++)
st[i].insert(), st[i].insert(R);
for(int i=;i<=k;i++) q2.push(R);
for(int i=;i<=k;i++) q[R].push();//
}
void frs(int u)
{
while(dq[u].size()&&dq[u].top()==q[u].top())
q[u].pop(), dq[u].pop();
}
void mdfy(int l,int r,int cr,int p,int k,bool fx)
{
if(l==r)
{
if(fx)dq[l].push(k); else q[l].push(k);
frs(l); mn[cr]=q[l].size()?q[l].top():R;
return;
}
int mid=l+r>>;
if(p<=mid)mdfy(l,mid,ls[cr],p,k,fx);
else mdfy(mid+,r,rs[cr],p,k,fx);
mn[cr]=Mn(mn[ls[cr]],mn[rs[cr]]);
}
int qry(int l,int r,int cr,int L,int R)
{
if(l>=L&&r<=R)return mn[cr];
int mid=l+r>>, ret=R;
if(L<=mid)ret=qry(l,mid,ls[cr],L,R);
if(mid<R)ret=Mn(ret,qry(mid+,r,rs[cr],L,R));
return ret;
}
void add(Node u)
{
int x=u.x, t=u.t;
it=st[t].lower_bound(x); it2=it; it2--;
mdfy(,R,,*it,*it2,); mdfy(,R,,*it,x,);
mdfy(,R,,x,*it2,);
if((*it2)==) dq2.push(*it), q2.push(x);
st[t].insert(x);
}
void del(Node u)
{
int x=u.x, t=u.t;
st[t].erase(st[t].lower_bound(x));
it=st[t].lower_bound(x); it2=it; it2--;
mdfy(,R,,*it,x,); mdfy(,R,,*it,*it2,);
mdfy(,R,,x,*it2,);
if((*it2)==) dq2.push(x), q2.push(*it);
}
int fnd(int u){return lower_bound(tp+,tp+R,u)-tp;}
void upd(Node u)
{
int x=u.x, id=u.id;
while(dq2.size()&&dq2.top()==q2.top())
dq2.pop(), q2.pop();
if(q2.top()==R){ ans[id]=-; return;}
int l=,r=Mx(tp[x],tp[R-]-tp[x]), ret;
while(l<=r)
{
int mid=l+r>>,p1=fnd(tp[x]-mid),p2=fnd(tp[x]+mid+);
int d=qry(,R,,p2,R);
if(d>=p1&&d<p2)ret=mid,r=mid-;
else l=mid+;
}
ans[id]=ret;
}
}T;
void build(int l,int r,int cr)
{
if(l==r)return; int mid=l+r>>;
ls[cr]=++tot; build(l,mid,ls[cr]);
rs[cr]=++tot; build(mid+,r,rs[cr]);
}
void ins(int l,int r,int cr,int L,int R,Node k)
{
if(l>=L&&r<=R){vt[cr].pb(k);return;}
int mid=l+r>>;
if(L<=mid)ins(l,mid,ls[cr],L,R,k);
if(mid<R)ins(mid+,r,rs[cr],L,R,k);
}
void dfs(int l,int r,int cr)
{
for(int i=,lm=vt[cr].size();i<lm;i++)
T.add(vt[cr][i]);
if(!ls[cr])
{
while(p0+<=Q&&qr[p0+].t==l)
T.upd(qr[++p0]);
}
else
{
int mid=l+r>>; dfs(l,mid,ls[cr]); dfs(mid+,r,rs[cr]);
}
for(int i=,lm=vt[cr].size();i<lm;i++)
T.del(vt[cr][i]);
}
int main()
{
n=rdn();k=rdn();Q=rdn();
for(int i=;i<=n;i++)
{
t[i].x=rdn(); t[i].t=rdn(); t[i].a=rdn(); t[i].b=rdn();
tp[++R]=t[i].a; tp[++R]=t[i].b;
}
for(int i=;i<=Q;i++)
{
qr[i].x=rdn(); qr[i].t=rdn();
qr[i].id=i; tp[++R]=qr[i].t;
}
sort(tp+,tp+R+); R=unique(tp+,tp+R+)-tp-;
for(int i=;i<=n;i++)
{
t[i].a=lower_bound(tp+,tp+R+,t[i].a)-tp;
t[i].b=lower_bound(tp+,tp+R+,t[i].b)-tp;
}
for(int i=;i<=Q;i++) qr[i].t=lower_bound(tp+,tp+R+,qr[i].t)-tp;
tot=; build(,R,); T.init();
for(int i=;i<=n;i++)
ins(,R,,t[i].a,t[i].b,Node(t[i].x,t[i].t));
sort(qr+,qr+Q+);
dfs(,R,);
for(int i=;i<=Q;i++)printf("%d\n",ans[i]);
return ;
}

还可以变成 nlogn 。就是把 “二分答案” 和 “线段树判断二分值” 合起来。https://loj.ac/article/523

就是直接在线段树上走。走到一个节点 ( l , mid , r ) ,判断接下来往哪个孩子走,即答案会在 [ l , mid ] 还是 [ mid+1 , r ] 。

如果询问位置 x 在 [ mid+1 , r ] 里面,就直接往右孩子走即可。

否则想让答案尽量小,就先看看答案能不能在 [ x , mid ] 里面。因为范围越大越容易合法,所以判断一下答案能不能是 mid 即可。

如果 mid 不合法,就有 mn( mid+1 , R ) < max{ 1 , x - ( mid - x ) } ;也就是 mn( mid+1 , R ) < 1 || mn( mid+1 , R ) + mid < 2*x 。

至于 mn( mid+1 , R ) ,如果当前的右孩子 rs 的范围是 [ mid+1 , R ] 的话,直接用线段树上节点存的值 mn[ ] 即可;

  如果右孩子的右边界不到 R 的话,可以知道之前一定有某一次,右边界从 =R 变成 <R 了,即走了左孩子;只要在每次走左孩子的时候,把那时的 mn[ rs ] 累计到 tmn 上,用 min{ tmn , mn[ rs ] } 判断即可。

这样最终走到的 l==r 的位置就是答案区间的右端点,返回 l - x 即可。

  也因为这样,如果出现过的最大位置是 mx 的话,线段树的范围应该到 2*mx 才行;比如询问点在最右边,某商店在最左边,答案区间的右端点就会大概是 2*mx 。

而且不用把线段树分治的那棵线段树建出来,直接把商店拆成一个加入操作和一个删除操作,同询问一起按时间排序即可。

  按时间排序的时候注意第二关键字是操作类型。同一时间应该先 删除/加入 再询问。

也不用把坐标离散化,动态开点即可。这样要注意没有 ls 或 rs 的情况,可以赋初值 mn[ 0 ] = INF 来解决。

判断无解的好方法是记录一个 Tpcnt 表示有多少种商店出现了;只要在加入或删除商店的时候看看它是不是该种类的唯一一个商店就能维护了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int N=3e5+,M=1e7;
int n,k,Q,R,tot,rt,ls[M],rs[M],mn[M],ans[N],Tpcnt;
multiset<int> st[N],q[M];
multiset<int>::iterator it,it2;
struct Node{
int x,t,bh,lx;
Node(int x=,int t=,int b=,int lx=):x(x),t(t),bh(b),lx(lx) {}
bool operator< (const Node &b)const
{return t==b.t?lx<b.lx:t<b.t;}///lx<b.lx!
}t[N*3];
void build(int l,int r,int &cr)
{
cr=++tot;
if(l==r){for(int i=;i<=k;i++)q[cr].insert(); return;}
int mid=l+r>>; build(mid+,r,rs[cr]);
}
void mdfy(int l,int r,int &cr,int p,int inc,int del)
{
if(!cr)cr=++tot;
if(l==r)
{
if(inc>=)q[cr].insert(inc);
if(del>=)q[cr].erase(q[cr].find(del));
mn[cr]=(q[cr].size()?*q[cr].begin():R);
return;
}
int mid=l+r>>;
if(p<=mid)mdfy(l,mid,ls[cr],p,inc,del);
else mdfy(mid+,r,rs[cr],p,inc,del);
mn[cr]=Mn(mn[ls[cr]],mn[rs[cr]]);
}
int qry(int x)
{
int l=,r=R,cr=rt, chk=x<<,tmn=R;//tmn! for [mid+1,R] not [rs]
while(l<r)
{
int mid=l+r>>, d=Mn(tmn,mn[rs[cr]]);
if(mid<x||d+mid<chk||d<)
l=mid+,cr=rs[cr];
else tmn=Mn(tmn,mn[rs[cr]]),r=mid,cr=ls[cr];
}
return l-x;
}
int main()
{
n=rdn();k=rdn();Q=rdn(); int cnt=;
for(int i=,x,tp,a,b;i<=n;i++)
{
x=rdn();tp=rdn();a=rdn();b=rdn();
R=Mx(R,x);
t[++cnt]=Node(x,a,tp,);
t[++cnt]=Node(x,b+,tp,);
}
for(int i=,x,tp;i<=Q;i++)
{
x=rdn();tp=rdn(); R=Mx(R,x);
t[++cnt]=Node(x,tp,i,);
}
/*R++;*/R=R<<|;
for(int i=;i<=k;i++)
st[i].insert(), st[i].insert(R);
build(,R,rt); mn[]=R;//mn[0]
sort(t+,t+cnt+);
for(int i=;i<=cnt;i++)
{
int x=t[i].x, bh=t[i].bh;
if(t[i].lx<=)
{
if(t[i].lx==)
{
it=st[bh].lower_bound(x); it2=it; it2--;
mdfy(,R,rt,x,*it2,-);
mdfy(,R,rt,*it,x,*it2);
if(st[bh].size()==)Tpcnt++;
st[bh].insert(x);
}
else
{
st[bh].erase(st[bh].find(x));//erase at first!
it=st[bh].lower_bound(x); it2=it; it2--;
mdfy(,R,rt,x,-,*it2);
mdfy(,R,rt,*it,*it2,x);
if(st[bh].size()==)Tpcnt--;
}
}
else ans[bh]=(Tpcnt==k?qry(x):-);
}
for(int i=;i<=Q;i++)printf("%d\n",ans[i]);
return ;
}

LOJ 2585 「APIO2018」新家 ——线段树分治+二分答案的更多相关文章

  1. LOJ #2585. 「APIO2018」新家

    #2585. 「APIO2018」新家 https://loj.ac/problem/2585 分析: 线段树+二分. 首先看怎样数颜色,正常的时候,离线扫一遍右端点,每次只记录最右边的点,然后查询左 ...

  2. 【LOJ】#2585. 「APIO2018」新家

    题解 成功把自己写自闭了 离散化之后再二分我是真不会算坐标啊我这个zz 先离散化所有坐标,然后对于每个位置维护一个最小前驱,然后线段树区间维护最小前驱 什么?位置一样?那就给每个大小为1的位置开个mu ...

  3. Solution -「洛谷 P5787」「模板」二分图(线段树分治)

    \(\mathcal{Description}\)   Link.    \(n\) 个结点的图,\(m\) 条形如 \((u,v,l,r)\) 的边,表示一条连接 \(u\) 和 \(v\) 的无向 ...

  4. LOJ 2586 「APIO2018」选圆圈——KD树

    题目:https://loj.ac/problem/2586 只会 19 分的暴力. y 都相等,仍然按直径从大到小做.如果当前圆没有被删除,那么用线段树把 [ x-r , x+r ] 都打上它的标记 ...

  5. 2018.11.01 loj#2319. 「NOIP2017」列队(线段树)

    传送门 唉突然回忆起去年去noipnoipnoip提高组试水然后省二滚粗的悲惨经历... 往事不堪回首. 所以说考场上真的有debuffdebuffdebuff啊!!!虽然当时我也不会权值线段树 这道 ...

  6. luogu P4632 [APIO2018] New Home 新家 线段树 set 二分

    写了一种比较容易理解 但是常数很大的sol. 容易发现可以扫描线. 维护好序列之后发现很难查距离 考虑二分. 这里二分可以在线段树上进行 当然可能存在一些问题 如果离散化的话需要处理一些比较麻烦的细节 ...

  7. 「CQOI2006」简单题 线段树

    「CQOI2006」简单题 线段树 水.区间修改,单点查询.用线段树维护区间\([L,R]\)内的所有\(1\)的个数,懒标记表示为当前区间是否需要反转(相对于区间当前状态),下方标记时懒标记取反即可 ...

  8. # 「NOIP2010」关押罪犯(二分图染色+二分答案)

    「NOIP2010」关押罪犯(二分图染色+二分答案) 洛谷 P1525 描述:n个罪犯(1-N),两个罪犯之间的仇恨值为c,m对仇恨值,求怎么分配使得两件监狱的最大仇恨值最小. 思路:使最大xxx最小 ...

  9. 【BZOJ4552】排序(线段树,二分答案)

    [BZOJ4552]排序(线段树,二分答案) 题面 BZOJ 题解 好神的题啊 直接排序我们做不到 怎么维护? 考虑一下,如果我们随便假设一个答案 怎么检验它是否成立? 把这个数设成\(1\),其他的 ...

随机推荐

  1. Example of Abstract Class

    The Vehicle class has abstract members that must be implemented by the Car class or any other class ...

  2. [工作日志] 2018-12-06 重点: 解决安全测试bug

    安全测试bug 业务端提出, 在修改是否给联系人发送短信接口,如果操作人和被操作的联系人不在同一个企业的情况下, 也是可以修改的. 解决办法: 加校验.

  3. python 中的堆 (heapq 模块)应用:Merge K Sorted Lists

    堆是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组对象.在队列中,调度程序反复提取队列中第一个作业并运行,因为实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短 ...

  4. MySQL中变量的使用

    一.认识MySQL 中的变量 在MySQL中变量的绝大部分的用处都是在存储过程和存储函数中. 当然也可以抛开存储过程和存储函数来单独使用. 变量在MySQL中的运用和在编程语言中的运用大体相同 二.M ...

  5. Linux用管道命令对文件的移动

    我的问题是这样的:我有一个文件夹,里面有大约有1000个文件,然后我想把这样的一部分文件给随机分成两部分,一部分含有100张,另外一部分含有剩下的所有的文件,这个时候如果是在Linux图形界面的话直接 ...

  6. event.currentTarget和event.target的区别

    currentTarget始终是监听事件者,而target是事件的真正发出者.

  7. hdoj-1022(栈的模拟)

    #include <iostream> #include <cstring> #include <algorithm> #include <stack> ...

  8. 在学习linux基础入门时的一些问题总结(1)

    本周在实验楼完成了<linux基础入门>的21个实验,虽然之前已经学习过linux的相关课程,对linux下的命令也有一些了解和实践,但完成这21个实验以及35个练习题仍然遇到了许多的问题 ...

  9. 实验吧—Web——WP之 Guess Next Session

    打开链接,他有给出查看原码的按钮,那么我们打开看看 在这个里面,如果GET的值等于session的就会给出flag 那么我们进行抓包改包 在输入框内随意输入一个值然后抓包 将password的值删去, ...

  10. 调试 shell script 方法

    wade@V1088:~$ cat b.sh#!/bin/bash dir=`pwd` dir=$dir'/' for f in `ls *.png` do echo $dir$f done 看每一行 ...