特殊四维偏序—星之河

特殊四维偏序-星之河

「DTOI-2」星之河

题目背景

星稀河影转,霜重月华孤。

题目描述

星之统治者有一个星盘,其可以被抽象为一棵根节点为 \(1\) 的树。树上每个节点 \(i\) 有一颗红星、一颗蓝星,亮度分别记为 \(\text{Red}_i,\text{Blue}_i\)

现在,星之统治者想要知道,对于每个节点 \(x\),其子树内(不包括该节点)有多少节点满足:其红星亮度小于等于 \(x\) 的红星亮度,且其蓝星亮度小于等于 \(x\) 的蓝星亮度。

你需要按编号顺序依次输出每个节点的答案。为减少输出量,如果答案为 \(0\) 则不必输出。

输入格式

第一行两个整数分别表示 \(n\)

接下来 \(n-1\) 行每行两个正整数 \(u,v\),表示存在 \((u,v)\) 这条树边。

接下来 \(n\) 行每行两个整数分别表示 \(\text{Red}_i, \text{Blue}_i\)

输出格式

每个答案非 \(0\) 的节点一行,每行一个整数表示答案。

样例 #1

样例输入 #1

10
2 1
3 1
4 3
5 1
6 4
7 2
8 2
9 4
10 3
3 1
2 4
-3 3
4 -2
-2 3
-3 -6
-5 -1
-4 -7
-5 -1
-7 -7

样例输出 #1

5
2
3
1

提示

样例解释

对于节点 \(1\),小于等于他的子节点有 \(6,7,8,9,10\),因此输出 \(5\)
对于节点 \(4\),小于等于他的子节点有 \(6\),因此输出 \(1\)
对于节点 $5 $ 至 \(10\),没有小于等于他的子节点,因此不输出。

数据范围

\(\textbf{Subtask}\) \(n\le\) 特殊性质 总分数
\(1\) \(1000\) \(10\)
\(2\) \(5\times 10^4\) \(20\)
\(3\) \(10^5\) \(-200\le \text{Red}_i, \text{Blue}_i \le 200\) \(20\)
\(4\) \(2\times 10^5\) 树的形态是链 \(20\)
\(5\) \(2\times 10^5\) \(30\)

对于所有数据,保证 \(n \le 2\times 10^5\)\(-10^9 \le \text{Red}_i, \text{Blue}_i \le 10^9\)

题解

纪念我考场上想出了正解却不敢写

这玩意在树上貌似不好搞,DP之类的都不行,考虑转移到序列上,那么我们把这棵树拍一个DFS序,然后再看这个问题

假设我们定义一个结构体

struct node{  
    int le,ri,b,r,id;
}a[N<<1];

分别存节点的DFS序的两个出现位置的权值,蓝色权值,红色权值以及节点\(id\),那么我们的问题就变成了对于每一个\(i\),统计满足

\[a[i].le\le a[j].le\le a[i].ri,a[j].b<a[i].b,a[j].r<a[i].r \]

的点的数量,假设我们记\(w(i,j)\)表示满足\(a[k].le<i,a[k].b<a[j].b,a[k].r<a[j].r\)\(k\)的个数

小小差分一下就有\(ans[i]=w(a[i].ri,i)-w(a[i].le-1,i)\)

那么对于\(w\)的求法,明眼人都看得出来,三维偏序

所以说,这道题本质上就是一个拍成DFS序之后的特殊四维偏序,这个四维偏序可以转化为两个三维偏序,也可以在累计答案的时候直接差分掉

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 200050
#define M 600050
#define re register
using namespace std;
struct node{
	int le,ri,b,r,id;
}a[N<<1];
int ans[N],num,head[N],ver[M],nxt[M],tot,c[N],n,m;
inline void add(int u,int v){
	nxt[++tot]=head[u],ver[head[u]=tot]=v;
}
void dfs(int u,int f){
	a[u].le=++num;
	for(int i=head[u];i;i=nxt[i]){
		int v=ver[i];
		if(v!=f)dfs(v,u);
	}
	a[u].ri=num;
}
inline bool cmp1(node a,node b){
	if(a.b==b.b){
		if(a.r==b.r)return a.le>b.le;;
		return a.r<b.r;
	}
	return a.b<b.b;
}
inline bool cmp2(node a,node b){
	if(a.r==b.r)return a.ri<b.ri;
	return a.r<b.r;
}
inline void Add(int x,int k){while(x<=n)c[x]+=k,x+=x&-x;}
inline int ask(int x){int ans=0;while(x)ans+=c[x],x-=x&-x;return ans;}
inline void CDQ(int l,int r){
	if(l==r)return ;
	int mid=l+r>>1;
	CDQ(l,mid);
	CDQ(mid+1,r);
	sort(a+l,a+mid+1,cmp2);
	sort(a+mid+1,a+r+1,cmp2);
	int j=l;
	for(re int i=mid+1;i<=r;i++){
		while(a[j].r<=a[i].r&&j<=mid)Add(a[j++].le,1);
		ans[a[i].id]+=ask(a[i].ri)-ask(a[i].le-1);
	}
	for(int i=l;i<j;i++)Add(a[i].le,-1);
}
inline void init(){
	scanf("%d",&n);
	for(re int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}
	dfs(1,0);
	for(re int i=1;i<=n;i++)scanf("%d%d",&a[i].b,&a[i].r);
	for(re int i=1;i<=n;i++)a[i].id=i;
	sort(a+1,a+n+1,cmp1);
	CDQ(1,n);
	for(re int i=1;i<=n;i++)if(ans[i])printf("%d\n",ans[i]);
}
int main(){
	init();
}
posted @ 2022-11-30 22:49  spdarkle  阅读(68)  评论(0编辑  收藏  举报