[DS记录]P6783 [Ynoi2008]rrusq

· · 个人记录

题意 : 在二维平面上,有 n 个关键点,m 个矩形,以及 q 个询问,每个关键点有权值 a_i

每次询问给定 [l,r],对于一个关键点 i ,如果点 i 在编号在 [l,r] 内的任意一个矩形中,则认为 i 被区间 [l,r] 的矩形并包含。

输出区间 [l,r] 的矩形并包含的所有关键点的权值和。

允许离线。

------------ - 首先考虑一维的子问题 : 区间线段并权值和。 考虑离线,从左到右加入线段,设 $t_i$ 为包含 $i$ 号点的编号最大的线段。 若加入到第 $r$ 条线段,查询 $[l,r]$ ,则所有 $l\leq t_i$ 的点都可以贡献。 设 $s[i]$ 为 $t=i$ 的点的权值和,其区间和即为答案。 每次加入一条线段,会使得一个区间的 $t_i$ 变成 $r$。 可以使用线段树维护,给一个区间打上标记,表示其中的 $t$ 同为某值。 显然,一个点上方恰有一个标记。 修改时,将标记下推,然后一口气将子树内所有标记回收掉,最后打上新的标记。 首先,若需要推标记,则表明子树内无标记,这样只会有 $O(\log n)$ 此标记的更新。 若子树内有标记,产生方法有两种 : - 推过后没有回收的。显然每次最多产生两个,左右各一个。 - 本来就在那里,从来没被碰过。 综上,这类区间最多 $O(n)$ 个。 每次回收标记和打标记会让一些点的 $t$ 改变,相当于对 $s$ 单点修改。 ------------ - 回到二维问题 我们使用 $\rm KDT$ 来把矩形拆成 $O(\sqrt{n})$ 个点的区间。 则一共会有 $O(n\sqrt{n})$ 次标记更新,配合 $O(1)-O(\sqrt{n})$ 的分块求和。 复杂度为 $O((n+q)\sqrt{n})$。 分块部分可以继续优化,但是由于常数小并没有必要。 ```cpp #include<algorithm> #include<cstdio> #include<vector> #include<cmath> #define pb push_back #define MaxN 100500 #define MaxM 1000500 using namespace std; struct SumDS{ int BS,c[MaxN],o[MaxN]; void add(int p,int x) {c[p]+=x;o[p/BS]+=x;} int qry(int l,int r) { int ret=0,bl=l/BS,br=r/BS; if (bl==br){ for (int i=l;i<=r;i++) ret+=c[i]; }else { for (int i=bl+1;i<br;i++)ret+=o[i]; bl=(bl+1)*BS;br=br*BS; for (int i=l;i<bl;i++)ret+=c[i]; for (int i=br;i<=r;i++)ret+=c[i]; }return ret; } }T; struct Node{int xl,yl,xr,yr,s,tg;}a[MaxN<<2]; struct Point{int x,y,v;}p[MaxN]; bool cmpX(const Point &A,const Point &B) {return A.x<B.x;} bool cmpY(const Point &A,const Point &B) {return A.y<B.y;} void build(int l,int r,int u,bool kd) { a[u].tg=-1; if (l==r){ a[u].xl=a[u].xr=p[l].x; a[u].yl=a[u].yr=p[l].y; a[u].s=p[l].v; return ; }int mid=(l+r)>>1; nth_element(p+l,p+mid,p+r+1,kd ? cmpX : cmpY); build(l,mid,u<<1,!kd); build(mid+1,r,u<<1|1,!kd); int ls=u<<1,rs=u<<1|1; a[u].s=a[ls].s+a[rs].s; a[u].xl=min(a[ls].xl,a[rs].xl); a[u].yl=min(a[ls].yl,a[rs].yl); a[u].xr=max(a[ls].xr,a[rs].xr); a[u].yr=max(a[ls].yr,a[rs].yr); } inline void ladd(int u) { if (a[u].tg>-1){ a[u<<1].tg=a[u].tg; a[u<<1|1].tg=a[u].tg; a[u].tg=-1; } } void clr(int u) { if (a[u].tg>-1){ T.add(a[u].tg,-a[u].s); a[u].tg=-1; return ; }clr(u<<1);clr(u<<1|1); } Node s[MaxM],wf; void mdf(int u=1) { if (wf.xr<a[u].xl||a[u].xr<wf.xl||wf.yr<a[u].yl|a[u].yr<wf.yl)return ; if (wf.xl<=a[u].xl&&a[u].xr<=wf.xr&&wf.yl<=a[u].yl&&a[u].yr<=wf.yr){ clr(u); T.add(a[u].tg=wf.tg,a[u].s); return ; }ladd(u); mdf(u<<1);mdf(u<<1|1); } struct Data{int l,p;}; vector<Data> b[MaxN]; int n,m,q,ans[MaxM]; int main() { scanf("%d",&n); T.BS=sqrt(n); for (int i=1;i<=n;i++){ p[i].x=i; scanf("%d%d",&p[i].y,&p[i].v); }build(1,n,1,0);a[1].tg=0; scanf("%d",&m); for (int i=1;i<=m;i++) scanf("%d%d%d%d",&s[i].xl,&s[i].xr,&s[i].yl,&s[i].yr); scanf("%d",&q); for (int i=1,l,r;i<=q;i++){ scanf("%d%d",&l,&r); b[r].pb((Data){l,i}); } for (int i=1;i<=m;i++){ wf=s[i];wf.tg=i;mdf(); for (int j=0;j<b[i].size();j++) ans[b[i][j].p]=T.qry(b[i][j].l,i); } for (int i=1;i<=q;i++) printf("%d\n",ans[i]); return 0; } ```