特殊四维偏序—星之河
特殊四维偏序-星之河
「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\),统计满足
的点的数量,假设我们记\(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();
}