• [做题笔记] 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("");
    }
    
  • 相关阅读:
    变量可变性问题
    Android 创建Listener监听器形式选择:匿名内部类?外部类?
    linux下安装zookeeper
    翻页工具类
    将哈夫曼树转化成二叉树
    Activity的启动流程分析
    题目1186:打印日期
    数据库设计--数据流图(DFD)
    c#基础之数组
    10.3.1 一个CONNECT BY的样例
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16422164.html
Copyright © 2020-2023  润新知