dtoi3733 New Home
题意:
五福街是一条笔直的道路,这条道路可以看成一个数轴,街上每个建筑物的坐标都可以用一个整数来表示。小明是一位时光旅行者,他知道在这条街上,在过去现在和未来共有 n个商店出现。第 i个商店可以使用四个整数 xi,ti,ai,bi描述,它们分别表示:商店的坐标、商店的类型、商店开业的年份、商店关闭的年份。
小明希望通过时光旅行,选择一个合适的时间,住在五福街上的某个地方。他给出了一份他可能选择的列表,上面包括了 q个询问,每个询问用二元组 (坐标,时间)表示。第 i对二元组用两个整数 li,yi描述,分别表示选择的地点 li和年份 yi。
现在,他想计算出在这些时间和地点居住的生活质量。他定义居住的不方便指数为:在居住的年份,离居住点最远的商店类型到居住点的距离。类型 t的商店到居住点的距离定义为:在指定的年份,类型 t的所有营业的商店中,到居住点距离最近的一家到居住点的距离。我们说编号为 i的商店在第 y年在营业当且仅当 ai≤y≤bi 。注意,在某些年份中,可能在五福街上并非所有 k种类型的商店都有至少一家在营业。在这种情况下,不方便指数定义为 -1。
你的任务是帮助小明求出每对(坐标,时间)二元组居住的不方便指数。
题解:
先将询问按照时间排序,离线来做。
这样我们按时间顺序插入/删除商店,对于一个询问,我们首先可以二分答案,接下来问题变成了求一段区间中有多少种不同的元素。那么这里有一个做法,就是将每一个点的权值设为nex[i],nex[i]表示下一个跟当前商店类型相同的商店的位置,然后插入线段树中,查询l,r中有多少nex[i]大于r。这可以树套树解决,但是分析复杂度,发现有3个log,显然是不可能过的。
再分析一下问题的性质,我们发现并不需要求有多少种不同的元素,只需要求有没有所有的元素即可。我们可以转变思路,只需要判断[0,l-1]中所有nex[i]的最大值是否小于等于r即可。这里有个边界,我们假定所有类型的商店每时每刻在0和Max+1的位置都分别开设着。这样我们就可以在两个log的时间内解决这道题,使用线段树维护即可。
但是本题有一个非常繁琐的东西,就是重复元素。我们需要用multiset在线段树的叶子中记录下元素用于删除插入,还要考虑新商店开设的位置重复的问题等等,所以也需要multiset维护。导致了常数的巨大无比,卡了好久才过。
#include<cstdio> #include<set> #include<vector> #include<algorithm> #include<cstdlib> using namespace std; const int INF=1e8; int n,k,m,ans[300002],cnt=1; typedef struct{ int Max,ls,rs; multiset<int>a; }P; typedef struct{ int x,t,a,b; }PP; typedef struct{ bool u; int x,t; }PPP; typedef struct{ int x,tim,num; }PPPP; bool cmp(PPPP aa,PPPP bb){ return (aa.tim<bb.tim); } P p[10000002]; PP d[300002]; multiset<int>q[300002]; vector<PPP>g[300002]; PPPP h[300002]; int lb(int z,int lef,int righ){ int mid; while(lef<righ) { mid=(lef+righ)/2; if (h[mid].tim>=z)righ=mid;else lef=mid+1; } return lef; } int ub(int z,int lef,int righ){ int mid; while(lef<righ) { mid=(lef+righ)/2; if (h[mid].tim<=z)lef=mid+1;else righ=mid; } return lef; } void gengxin(int root,int begin,int end,int wz,int z,int th,bool u){ if (begin==end) { if (!u) { multiset<int>::iterator it;it=p[root].a.find(z); p[root].a.erase(it); if (th!=-1)p[root].a.insert(th); } else p[root].a.insert(z); if (p[root].a.empty())p[root].Max=0; else { multiset<int>::iterator it=p[root].a.end();it--; p[root].Max=*it; } return; } int mid=(begin+end)/2; if (wz<=mid) { if (!p[root].ls)p[root].ls=++cnt; gengxin(p[root].ls,begin,mid,wz,z,th,u); } else { if (!p[root].rs)p[root].rs=++cnt; gengxin(p[root].rs,mid+1,end,wz,z,th,u); } p[root].Max=max(p[p[root].ls].Max,p[p[root].rs].Max); } int chaxun(int root,int begin,int end,int begin2,int end2){ if (!root || begin>end2 || end<begin2)return 0; if (begin>=begin2 && end<=end2)return p[root].Max; int mid=(begin+end)/2; return max(chaxun(p[root].ls,begin,mid,begin2,end2),chaxun(p[root].rs,mid+1,end,begin2,end2)); } int read(){ int f=0;char ch=getchar(); while(ch<'0' || ch>'9')ch=getchar(); while(ch>='0' && ch<='9'){f=f*10+ch-48;ch=getchar();} return f; } int main() { n=read();k=read();m=read(); for (int i=1;i<=n;i++) { d[i].x=read();d[i].t=read();d[i].a=read();d[i].b=read(); } for (int i=1;i<=m;i++) { h[i].x=read();h[i].tim=read();h[i].num=i; } sort(h+1,h+m+1,cmp); for (int i=1;i<=n;i++) { int l=lb(d[i].a,1,m),r=ub(d[i].b,1,m+1); if (l==r)continue; PPP aa;aa.u=1;aa.x=d[i].x;aa.t=d[i].t; g[l].push_back(aa); aa.u=0; g[r].push_back(aa); } for (int i=1;i<=k;i++) { q[i].insert(0);q[i].insert(INF+1); gengxin(1,0,INF+1,0,INF+1,0,1); } for (int i=1;i<=m;i++) { for (int j=0;j<g[i].size();j++) { int tt=g[i][j].t,xx=g[i][j].x; if (g[i][j].u) { multiset<int>::iterator ls,nx; nx=ls=q[tt].lower_bound(xx);ls--; if (*nx!=xx) { gengxin(1,0,INF+1,*ls,*nx,xx,0); gengxin(1,0,INF+1,xx,*nx,0,1); } q[tt].insert(xx); } else { multiset<int>::iterator ls,nx,it; it=q[tt].find(xx); nx=ls=q[tt].lower_bound(xx);nx++; if (*nx!=xx) { ls--; gengxin(1,0,INF+1,*ls,xx,*nx,0); gengxin(1,0,INF+1,xx,*nx,-1,0); } q[tt].erase(it); } } int lef=0,righ=max(h[i].x-1,INF-h[i].x)+1,mid; while(lef<righ) { mid=(lef+righ)/2; int l=max(h[i].x-mid,1),r=min(h[i].x+mid,INF); if (chaxun(1,0,INF+1,0,l-1)<=r)righ=mid;else lef=mid+1; } if (lef!=max(h[i].x-1,INF-h[i].x)+1)ans[h[i].num]=lef;else ans[h[i].num]=-1; } for (int i=1;i<=m;i++)printf("%d\n",ans[i]); return 0; }