摸×2

没错这又是shr学长来的一天。

上次讲了平衡树,这次讲了二维数点。

二维数点

二维数点,就是在二维平面上数点的个数

一般是把一维在线操作转化为二维离线的操作,查询的时间就是新的一维(也有其他的转化)


呃啊


比如,给出一个序列\(a\),求对于\(\forall i\in [l,r]\),满足\(a_{i}\leqslant x\)的个数。

洛谷原题链接

这里就是将下标\(i\)作为一个维度,将值\(a_{i}\)作为另一个维度,每次相当于查询满足\(x\in [l,r],y\in [-\infty,x]\)\((x,y)\)的个数。

联想到前缀和,要求这个矩阵的点数,相当于是四个四分之一平面的答案经过一番加减之后的结果。

(这题中更简单,直接用两个查询相减即可,也就是将一个询问拆成俩\(query(l,r)\)变成\(query(r)-query(l-1)\),眼熟眼熟)

道理我都懂,但我还是不知道怎么实现啊

引入一个我不会的扫描线

之前一直听扫描线啥的,这啊那啊的,我现在终于知道是咋扫的了www

扫描线:每次根据一个维度进行有序扫描,扫描的过程中进行操作。

在这道题中就是扫描下标,每次有两种操作:加点,和查询。加点就是把\(a_{i}\)加进去,查询就是查询当前满足\(a_{i}\leqslant x\)的个数,这用树状数组是好维护的。但有些查询对答案是负贡献,所以离线处理的时候也需要存储贡献正负。

然后就有了

ans[q[i][j].id]+=_sum(q[i][j].x)*q[i][j].v;

然后就有了

二维数点板子代码(?)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+5;
int n,m;
int a[N];
struct node{
	int x,id,v;
};
vector <node> q[N];
int tr[N];
int ans[N];

int lowbit (int x) {  return x&-x;  }
void _update(int d,int x)
{
	while (d<N) tr[d]+=x,d+=lowbit(d);
}
int _sum(int x)//查询
{
	int res=0;
	while (x) res+=tr[x],x-=lowbit(x);
	return res;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for (int i=1,l,r,x;i<=m;i++)
	{
		scanf("%lld%lld%lld",&l,&r,&x);
		q[l-1].push_back((node){x,i,-1}),q[r].push_back((node){x,i,1});
	}
	
	for (int i=1;i<=n;i++)
	{
		_update(a[i],1);
		int _size=q[i].size();
		for (int j=0;j<_size;j++) ans[q[i][j].id]+=_sum(q[i][j].x)*q[i][j].v;
	}
	
	for (int i=1;i<=m;i++) printf("%lld\n",ans[i]);
	return 0;
}

呃啊


再比如P8844小卡与落叶

这题也可以二维数点做。

每次查询如下图↓

刚开始跑一个dfs记录\(dfn\)序、子树大小、深度等。

对于\(m\)次操作,注意到对于每个操作2,只有上一个操作1会对它产生影响,所以将会对该操作产生影响的操作1与操作2捆绑在一起。

每次按照\(dep\)降序扫描,先将该层的点都加进去,再计算储存的操作的贡献。储存操作是按照操作1中的深度储存的而不是操作2的

基本上一遍过的代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m;
vector <int> tr[N];
struct node {  int id,dfn,v;  };
vector <node> q[N];
vector <int> add[N];
int tme;
int mx,cnt;
int t[N]; 
int ans[N];

int dfn[N],dep[N],siz[N];//dfs序,深度,子树大小 
void dfs(int x,int _fa,int _dep)
{
	mx=max(mx,_dep);
	dfn[x]=++tme,dep[x]=_dep,siz[x]=1;
	add[_dep].push_back(dfn[x]);
	
	int _size=tr[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=tr[x][i];
		if (v==_fa) continue;
		dfs(v,x,_dep+1);
		siz[x]+=siz[v];
	}
}
int lowbit(int x) {  return x&-x;  }
void _update(int d,int x)
{
	while (d<N) t[d]+=x,d+=lowbit(d);
}
int _sum(int x)
{
	int res=0;
	while (x) res+=t[x],x-=lowbit(x);
	return res;
} 
signed main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1,u,v;i<n;i++)
	{
		scanf("%lld%lld",&u,&v);
		tr[u].push_back(v);
		tr[v].push_back(u);
	}
	
	dfs(1,0,1);
	
	for (int i=1,op,x,pre;i<=m;i++)
	{
		scanf("%lld%lld",&op,&x);
		if (op==1) pre=x;
		if (op==2)
		{
			q[pre].push_back({++cnt,dfn[x]+siz[x]-1,1});
			q[pre].push_back({cnt,dfn[x]-1,-1});
		}
	}
	
	for (int i=mx;i>=1;i--)
	{
		int _size1=add[i].size();
		for (int j=0;j<_size1;j++) _update(add[i][j],1);
		
		int _size2=q[i].size();
		for (int j=0;j<_size2;j++) ans[q[i][j].id]+=_sum(q[i][j].dfn)*q[i][j].v;
	}
	
	for (int i=1;i<=cnt;i++) printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2024-11-24 22:44  还是沄沄沄  阅读(11)  评论(2编辑  收藏  举报