摸×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;
}