鲜花:树套树求解三维偏序

由于模拟赛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求解高维偏序。

posted @ 2024-11-04 15:41  一位很会的教授er~  阅读(53)  评论(4编辑  收藏  举报