鲜花:树套树求解三维偏序
由于模拟赛T4 \(O(n\log^2n )\) 的官方题解需要用到三维偏序所以就学习了·一下,感觉会是很实用的东西。
模板题题意:给你 \(n\) 个点,坐标为 \((x_i,y_i,z_i)\),设 \(f(i)=\sum\limits_{j=1}^{n}[i\neq j\land x_j\le x_i\land y_j\le y_i\land z_j\le z_i]\),设 \(g(i)=\sum\limits_{j=1}^{n}[f(j)=i]\)。对于 \(i\in[0,n-1]\),求出 \(g(i)\)。其中 \(\land\) 表示且。
虽然这题大部分人是拿CDQ分治打的,但是我太蒻了,没学CDQ分治,在翻看题解区理性分析时发现bitset的 \(O(\tfrac{n^2m}{w})\)的做法很难过得去,于是决定打一棵树套树。鉴于空间紧俏,于是外层树果断开树状数组,内层树选择Treap。第一维直接排序忽略掉。第二维使用树状数组维护前缀,对树状数组的每个节点开一棵Treap,维护树状数组上对应的区间第三维信息。修改直接对树状数组上对应区间的Treap插入数,查询直接在树状数组上对应区间的Treap查询排名加和。注意你要真对每一个区间开一棵满Treap必定MLE,所以要把这 \(n\) 个Treap封到同一个结构体里,然后分开储存根节点。注意题目不保证没有重复的点。没了。处理好重复的点之后一遍过。
教授の代码
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define inf 0x3f3f3f3f
namespace rander
{
mt19937_64 rad(time(0));
template<typename tn> il tn rand(tn x=-inf,tn y=inf)
{
return rad()%(y-x+1)+x;
}
}using namespace rander;
int a,b,ans[200002];
struct trible
{
int first,second,third;
bool operator <(const trible &A)const
{
if(first!=A.first)
{
return first<A.first;
}
if(second!=A.second)
{
return second<A.second;
}
return third<A.third;
}
}que[200002];
struct Binary_Index_Tree
{
#define N 200002
int size;
struct Treap
{
#define M 4000004
int cnt,root[N],pri[M],siz[M],val[M],ls[M],rs[M];
il void pushup(int x)
{
siz[x]=siz[ls[x]]+siz[rs[x]]+1;
}
il int make(int x)
{
cnt++;
val[cnt]=x;
pri[cnt]=rand();
siz[cnt]=1;
return cnt;
}
il int merge(int x,int y)
{
if(!x||!y)
{
return x|y;
}
if(pri[x]<pri[y])
{
rs[x]=merge(rs[x],y);
pushup(x);
return x;
}
else
{
ls[y]=merge(x,ls[y]);
pushup(y);
return y;
}
}
il pair<int,int> split(int x,int y)
{
if(!x)
{
return {0,0};
}
register pair<int,int> rn;
if(val[x]<y)
{
rn=split(rs[x],y);
rs[x]=rn.first;
rn.first=x;
}
else
{
rn=split(ls[x],y);
ls[x]=rn.second;
rn.second=x;
}
pushup(x);
return rn;
}
il void insert(int x,int y)
{
pair<int,int> z=split(root[x],y);
root[x]=merge(merge(z.first,make(y)),z.second);
}
il int rank(int x,int y)
{
pair<int,int> z=split(root[x],y);
ri rn=siz[z.first]+1;
root[x]=merge(z.first,z.second);
return rn;
}
#undef M
}t;
il int lowbit(int x)
{
return x&-x;
}
il void add(int x,int y)
{
while(x<=size)
{
t.insert(x,y);
x+=lowbit(x);
}
}
il int find(int x,int y)
{
ri rn=0;
while(x)
{
rn+=max(t.rank(x,y+1)-1,0);
x-=lowbit(x);
}
return rn;
}
#undef N
}bit;
int main()
{
scanf("%d%d",&a,&b);
for(ri i=1;i<=a;i++)
{
scanf("%d%d%d",&que[i].first,&que[i].second,&que[i].third);
}
sort(que+1,que+1+a);
bit.size=b;
ri re=1;
bit.add(que[1].second,que[1].third);
for(ri i=2;i<=a;i++)
{
if(que[i].first!=que[re].first)
{
for(ri j=re;j<=i-1;j++)
{
ans[bit.find(que[j].second,que[j].third)-1]++;
}
re=i;
}
bit.add(que[i].second,que[i].third);
}
for(ri i=re;i<=a;i++)
{
ans[bit.find(que[i].second,que[i].third)-1]++;
}
for(ri i=0;i<a;i++)
{
printf("%d\n",ans[i]);
}
return 0;
}
bitset的最大用处不在这里,在强制在线或是更高维度的偏序,无论是CDQ还是树套树都无法解决的领域。下一次就发bitset求解高维偏序。