[做题笔记] lxl 的数据结构选讲(下)

rrusq

题目描述

点此看题

解法

考虑扫描矩形的右端点,维护所有左端点的答案。

考虑加入一个矩形的影响,可以用染色模型来理解,也就是把在这个矩形内的点染成这个矩形编号的颜色,那么查询只需要找颜色 \(\geq l\) 的点权值和即可。

考虑如何快速染色并且维护点权,一个显然的想法是颜色段均摊,因为本题是二维意义下的,所以我们可以在 \(\tt kdt\) 上颜色段均摊。具体来说就是在给一个点打标记时,直接暴力清空在子树中的标记,颜色标记需要在递归前下传。

打标记时可以拿一个全局的数据结构来支持修改和点权查询,我们可以使用 \(O(1)-O(\sqrt n)\) 的分块技术。

注意本题操作的特殊性,\(\tt kdt\) 要类似线段树来建立(非叶节点代表区间,不代表实际的点);复杂度基于,对于每个矩形打标记的时间和删除标记的时间是一样的,所以总时间复杂度 \(O(m\sqrt n+q\sqrt m)\)

#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int M = 100005;
const int Z = M<<2;
const int N = 1000005;
const int inf = 0x3f3f3f3f;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void write(int x)
{
	if(x>=10) write(x/10);
	putchar(x%10+'0');
}
int n,m,q,A[Z],B[Z],C[Z],D[Z];
int xl,xr,yl,yr,s[Z],col[Z],vis[Z];
struct node{int x,y,v;}a[M];vector<int> v[M];
struct hhz{int a,b,c,d;}t[M];int ans[N],L[N];
struct zxy
{
	int n,a[M],b[M],fl[M],L[M],R[M];
	void init()
	{
		int m=sqrt(n);
		for(int i=1;i<=n;i++)
		{
			b[i]=(i-1)/m+1;
			if(!L[b[i]]) L[b[i]]=i;
			R[b[i]]=i;
		}
	}
	void add(int x,int y) {a[x]+=y;fl[b[x]]+=y;}
	int ask(int x)
	{
		int r=0;
		for(int i=b[x]+1;i<=b[n];i++) r+=fl[i];
		for(int i=x;i<=R[b[x]];i++) r+=a[i];
		return r;
	}
}H;
void build(int x,int l,int r,int w)
{
	if(l==r)
	{
		s[x]=a[l].v;
		A[x]=B[x]=a[l].x;
		C[x]=D[x]=a[l].y;
		return ;
	}
	int mid=(l+r)>>1;
	nth_element(a+l,a+mid,a+1+r,[&](node u,node v)
	{return w==0?(u.x<v.x):(u.y<v.y);});
	build(x<<1,l,mid,w^1);
	build(x<<1|1,mid+1,r,w^1);
	A[x]=min(A[x<<1],A[x<<1|1]);
	B[x]=max(B[x<<1],B[x<<1|1]);
	C[x]=min(C[x<<1],C[x<<1|1]);
	D[x]=max(D[x<<1],D[x<<1|1]);
	s[x]=s[x<<1]+s[x<<1|1];
}
int out(int x)
{
	return A[x]>xr || B[x]<xl || C[x]>yr || D[x]<yl;
}
int in(int x)
{
	return xl<=A[x] && B[x]<=xr && yl<=C[x] && D[x]<=yr;
}
void down(int x)
{
	if(!col[x]) return ;
	col[x<<1]=col[x<<1|1]=col[x];
	vis[x<<1]=vis[x<<1|1]=1;col[x]=0;
}
void clr(int x)
{
	if(!vis[x]) return ;vis[x]=0;
	if(col[x])
	{
		H.add(col[x],-s[x]);
		col[x]=0;return ;
	}
	clr(x<<1);clr(x<<1|1);
}
void dfs(int x,int y)
{
	if(out(x)) return ;
	if(in(x))
	{
		clr(x);col[x]=y;vis[x]=1;
		H.add(y,s[x]);return ;
	}
	down(x);dfs(x<<1,y);dfs(x<<1|1,y);
	vis[x]=1;
}
signed main()
{
	n=read();A[0]=C[0]=inf;
	for(int i=1;i<=n;i++)
		a[i].x=i,a[i].y=read(),a[i].v=read();
	build(1,1,n,0);
	m=read();H.n=m;H.init();
	for(int i=1;i<=m;i++)
		t[i].a=read(),t[i].b=read(),
		t[i].c=read(),t[i].d=read();
	q=read();
	for(int i=1;i<=q;i++)
		L[i]=read(),v[read()].push_back(i);
	for(int i=1;i<=m;i++)
	{
		xl=t[i].a;xr=t[i].b;
		yl=t[i].c;yr=t[i].d;
		dfs(1,i);
		for(int x:v[i]) ans[x]=H.ask(L[x]);
	}
	for(int i=1;i<=q;i++)
		write(ans[i]),puts("");
}

rprmq1

题目描述

点此看题

解法

二维平面可以视为有 \(n\) 个版本的,长度为 \(n\) 的序列。设修改为五元组 \((l,r,x,y,c)\),可以把它拆分成两部分,把 \((x,y,c)\) 放在 \(l\) 处,把 \((x,y,-c)\) 放在 \(r+1\) 处。对于一个版本,我们只需要加入放在它前面的所有修改即可。

那么对于一个询问 \((l,r,x,y)\),我们需要在 \([l,r]\) 这些版本中询问区间 \([x,y]\) 的最大值。

考虑猫树分治,对于过终点的询问,可以把它拆成两部分,然后维护历史最大值即可。猫树分治的作用在于:提供了一个版本的起点,把若干个版本分别求最大值的问题转化成了历史最大值问题

先拿从中点向右扫来举例(中点向左扫类似),设现在访问的到的版本是 \(x\),我们需要记录 \((mid,x]\) 这些版本的历史最大值。可以在开始扫的时候把历史最大值清空(线段树上维护清空标记来实现),扫描的时候线段树维护历史最大值即可。

由于需要提前加入位于 \([1,l)\) 的这些修改,所以需要维护一个全局的指针,时间复杂度 \(O(n\log^2 n+q\log n)\)

#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 500005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void write(int x)
{
	if(x>=10) write(x/10);
	putchar(x%10+'0');
}
int n,m,q,ans[M];
struct zxy{int id,l,r,x,y;}a[M];vector<zxy> b[M];
struct node{int l,r,c;};vector<node> v[M];
//
int fl[M],hf[M],mx[M],hm[M],cov[M];
void add(int i,int v1,int v2)
{
	hm[i]=max(hm[i],v2+mx[i]);mx[i]+=v1;
	hf[i]=max(hf[i],v2+fl[i]);fl[i]+=v1;
}
void reset(int i)
{
	add(i<<1,fl[i],hf[i]);
	add(i<<1|1,fl[i],hf[i]);
	fl[i]=hf[i]=0;hm[i]=mx[i];cov[i]=1;
}
void down(int i)
{
	if(cov[i]) reset(i<<1),reset(i<<1|1),cov[i]=0;
	add(i<<1,fl[i],hf[i]);
	add(i<<1|1,fl[i],hf[i]);
	fl[i]=hf[i]=0;
}
void up(int i)
{
	mx[i]=max(mx[i<<1],mx[i<<1|1]);
	hm[i]=max(hm[i<<1],hm[i<<1|1]);
}
void Add(int i,int l,int r,int L,int R,int c)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R) {add(i,c,c);return ;}
	int mid=(l+r)>>1;down(i);
	Add(i<<1,l,mid,L,R,c);
	Add(i<<1|1,mid+1,r,L,R,c);
	up(i);
}
int ask(int i,int l,int r,int L,int R)
{
	if(L>r || l>R) return 0;
	if(L<=l && r<=R) return hm[i];
	int mid=(l+r)>>1;down(i);
	return max(ask(i<<1,l,mid,L,R),
	ask(i<<1|1,mid+1,r,L,R));
}
//
void ins(int i,int l,int r,zxy p)
{
	int mid=(l+r)>>1;
	if(p.l<=mid+1 && mid<=p.r)
		{b[i].push_back(p);return ;}
	if(p.r<=mid) ins(i<<1,l,mid,p);
	else ins(i<<1|1,mid+1,r,p);
}
int cmp(node a,node b) {return a.c<b.c;}
int cmp1(zxy a,zxy b) {return a.r<b.r;}
int cmp2(zxy a,zxy b) {return a.l>b.l;}
void get(int i,int op)
{
	if(op==1) for(auto x:v[i])
		Add(1,1,n,x.l,x.r,x.c);
	else if(v[i].size())
		for(int j=v[i].size()-1;j>=0;j--)
			Add(1,1,n,v[i][j].l,v[i][j].r,-v[i][j].c);
}
void dfs(int i,int l,int r)
{
	int mid=(l+r)>>1,k=0,j=1;
	for(int i=l;i<=mid;i++) get(i,1);
	for(auto x:b[i]) a[++k]=x;
	sort(a+1,a+1+k,cmp1);
	while(j<=k && a[j].r==mid) j++;
	for(int i=mid+1;i<=r;i++)
	{
		get(i,1);
		if(i==mid+1) reset(1);
		while(j<=k && a[j].r==i)
		{
			ans[a[j].id]=max(ans[a[j].id],
			ask(1,1,n,a[j].x,a[j].y));j++;
		}
	}
	for(int i=r;i>=mid+1;i--) get(i,-1);
	if(l!=r) dfs(i<<1|1,mid+1,r);
	//
	k=0;j=1;
	for(auto x:b[i]) a[++k]=x;
	sort(a+1,a+1+k,cmp2);
	while(j<=k && a[j].l==mid+1) j++;
	for(int i=mid;i>=l;i--)
	{
		if(i==mid) reset(1);
		while(j<=k && a[j].l==i)
		{
			ans[a[j].id]=max(ans[a[j].id],
			ask(1,1,n,a[j].x,a[j].y));j++;
		}
		get(i,-1);
	}
	if(l!=r) dfs(i<<1,l,mid);
}
signed main()
{
	n=read();m=read();q=read();
	for(int i=1;i<=m;i++)
	{
		int l=read(),x=read(),r=read(),y=read(),c=read();
		v[l].push_back(node{x,y,c});
		v[r+1].push_back(node{x,y,-c});
	}
	for(int i=1;i<=n;i++)
		sort(v[i].begin(),v[i].end(),cmp);
	for(int i=1;i<=q;i++)
	{
		int l=read(),x=read(),r=read(),y=read();
		ins(1,1,n,zxy{i,l,r,x,y});
	}
	dfs(1,1,n);
	for(int i=1;i<=q;i++) write(ans[i]),puts("");
}
posted @ 2022-06-29 09:55  C202044zxy  阅读(579)  评论(0编辑  收藏  举报