博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LOJ 2585 「APIO2018」新家 ——线段树分治+二分答案
阅读量:5967 次
发布时间:2019-06-19

本文共 5785 字,大约阅读时间需要 19 分钟。

题目:

算答案的时候要二分!

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

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

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

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

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

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

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

#include
#include
#include
#include
#include
#include
#define pb push_backusing namespace std;int rdn(){ int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret;}int Mn(int a,int b){ return a
b?a:b;}const int N=3e5+5,M=3*N,M2=N<<1;//M:time M2:posint n,k,Q,R,tp[M],tot,ls[M<<1],rs[M<<1];int p0,ans[N];struct Sp{ int x,t,a,b;}t[N];struct Node{ int x,t,id; Node(int x=0,int t=0):x(x),t(t) {} bool operator< (const Node &b)const { return t
vt[M<<1];struct Tr{ int R,ls[M2<<1],rs[M2<<1],mn[M2<<1]; multiset
st[N]; multiset
::iterator it,it2; priority_queue
,greater
> q[M2],dq[M2]; priority_queue
q2,dq2; void build(int l,int r,int cr) { mn[cr]=R; if(l==r)return; int mid=l+r>>1; ls[cr]=++tot; build(l,mid,ls[cr]); rs[cr]=++tot; build(mid+1,r,rs[cr]); } void init() { for(int i=1;i<=n;i++) tp[++R]=t[i].x; for(int i=1;i<=Q;i++) tp[++R]=qr[i].x; sort(tp+1,tp+R+1); R=unique(tp+1,tp+R+1)-tp-1; for(int i=1;i<=n;i++) t[i].x=lower_bound(tp+1,tp+R+1,t[i].x)-tp; for(int i=1;i<=Q;i++) qr[i].x=lower_bound(tp+1,tp+R+1,qr[i].x)-tp; R++; tot=1; build(1,R,1); for(int i=1;i<=k;i++) st[i].insert(0), st[i].insert(R); for(int i=1;i<=k;i++) q2.push(R); for(int i=1;i<=k;i++) q[R].push(0);// } 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>>1; if(p<=mid)mdfy(l,mid,ls[cr],p,k,fx); else mdfy(mid+1,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>>1, ret=R; if(L<=mid)ret=qry(l,mid,ls[cr],L,R); if(mid
>1,p1=fnd(tp[x]-mid),p2=fnd(tp[x]+mid+1); int d=qry(1,R,1,p2,R); if(d>=p1&&d
>1; ls[cr]=++tot; build(l,mid,ls[cr]); rs[cr]=++tot; build(mid+1,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>>1; if(L<=mid)ins(l,mid,ls[cr],L,R,k); if(mid
>1; dfs(l,mid,ls[cr]); dfs(mid+1,r,rs[cr]); } for(int i=0,lm=vt[cr].size();i
View Code

还可以变成 nlogn 。就是把 “二分答案” 和 “线段树判断二分值” 合起来。

就是直接在线段树上走。走到一个节点 ( 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
#include
#include
#include
using namespace std;int rdn(){ int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){
if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',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
st[N],q[M];multiset
::iterator it,it2;struct Node{ int x,t,bh,lx; Node(int x=0,int t=0,int b=0,int lx=0):x(x),t(t),bh(b),lx(lx) {} bool operator< (const Node &b)const { return t==b.t?lx
>1; build(mid+1,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>=0)q[cr].insert(inc); if(del>=0)q[cr].erase(q[cr].find(del)); mn[cr]=(q[cr].size()?*q[cr].begin():R); return; } int mid=l+r>>1; if(p<=mid)mdfy(l,mid,ls[cr],p,inc,del); else mdfy(mid+1,r,rs[cr],p,inc,del); mn[cr]=Mn(mn[ls[cr]],mn[rs[cr]]);}int qry(int x){ int l=1,r=R,cr=rt, chk=x<<1,tmn=R;//tmn! for [mid+1,R] not [rs] while(l
>1, d=Mn(tmn,mn[rs[cr]]); if(mid
<1) l=mid+1,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=0; for(int i=1,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,0); t[++cnt]=Node(x,b+1,tp,1); } for(int i=1,x,tp;i<=Q;i++) { x=rdn();tp=rdn(); R=Mx(R,x); t[++cnt]=Node(x,tp,i,2); } /*R++;*/R=R<<1|1; for(int i=1;i<=k;i++) st[i].insert(0), st[i].insert(R); build(1,R,rt); mn[0]=R;//mn[0] sort(t+1,t+cnt+1); for(int i=1;i<=cnt;i++) { int x=t[i].x, bh=t[i].bh; if(t[i].lx<=1) { if(t[i].lx==0) { it=st[bh].lower_bound(x); it2=it; it2--; mdfy(1,R,rt,x,*it2,-1); mdfy(1,R,rt,*it,x,*it2); if(st[bh].size()==2)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(1,R,rt,x,-1,*it2); mdfy(1,R,rt,*it,*it2,x); if(st[bh].size()==2)Tpcnt--; } } else ans[bh]=(Tpcnt==k?qry(x):-1); } for(int i=1;i<=Q;i++)printf("%d\n",ans[i]); return 0;}

 

转载于:https://www.cnblogs.com/Narh/p/10457013.html

你可能感兴趣的文章
Vertically aligning HTML
查看>>
Linux之cut命令
查看>>
jedis 用连接池时超时返回值类型错误
查看>>
nginx 查看每秒有多少访问量
查看>>
python正则表达式
查看>>
安装nagios中php安装报错 configure error xml2-config not foud
查看>>
php邮件发送类
查看>>
Python算法题----在列表中找到和为s的两个数字
查看>>
《深入理解Java虚拟机》(二)java虚拟机运行时数据区
查看>>
分布式日志平台--ELKStack实践
查看>>
互联网思维
查看>>
ecshop备份数据 ecshop转移数据 ecshop更换主机
查看>>
手机将与瘦客户机争夺办公桌面
查看>>
ubuntu下针对php的thrift 安装折腾记录
查看>>
使用C#客户端访问FTP服务的一个解决方案
查看>>
对软件测试团队“核心价值”的思考
查看>>
pthread
查看>>
深入理解html5系列-文本标签
查看>>
mysql基础知识点
查看>>
Microsoft.System.Center.Operations.Manager.2007 中文版完整光盘下载地址
查看>>