【题解 P8575】 星之河
「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\)。
解题思路
此题有些偏模板。
分析题意,就是找每个节点的子树中有多少个节点 \(red\) 和 \(blue\) 都小于自己。
利用欧拉序给每个节点编号,那么一个节点 \(x\) 的子树范围就是 \(dfn_x+1\) 到 \(out_x\) ,不包获该节点。
可以转化为 \(1\) 到 \(dfn_x+1\) 与 \(out_x\) 中,满足要求的有多少个,相减即可。
多个询问,每次求节点号,\(red\) ,\(blue\) 都小于自己的有多少个节点,三维偏序问题。
\(CDQ\) 分治解决,先在分治外确定完一维,分治内部合并时排序第二维,最后一维用树状数组即可。
时间复杂度 \(O(nlog^2n)\) 。
Code
#include<bits/stdc++.h>
using namespace std;
struct datay
{
int p,x,y,op,v;
}a[600005],b[300005];
int n,dfn[200005],out[200005],num,m,f[200005];
vector<int> t[200005];
map<int,int> p;
set<int> g;
bool cmp1(datay q,datay w)
{
if(q.p!=w.p)return q.p<w.p;
return q.op<w.op;
}
bool cmp2(datay q,datay w)
{
if(q.x!=w.x)return q.x<w.x;
return q.op<w.op;
}
bool cmp3(datay q,datay w)
{
return q.op<w.op;
}
int lowbit(int x)
{
return x&(-x);
}
void clear(int x)
{
for(int i=x;i<=n;i+=lowbit(i))f[i]=0;
return;
}
void dijah(int x,int y)
{
for(int i=x;i<=n;i+=lowbit(i))f[i]+=y;
return;
}
int gaia(int x)
{
int h=0;
while(x)
{
h+=f[x];
x-=lowbit(x);
}
return h;
}
void dfs(int x,int y)
{
dfn[x]=++num;
for(int i=0;i<t[x].size();i++)
{
if(t[x][i]==y)continue;
dfs(t[x][i],x);
}
out[x]=num;
return;
}
void cdq(int l,int r)
{
if(l==r)
{
return;
}
int mid=(l+r)>>1,x=l,y,z;
cdq(l,mid);
cdq(mid+1,r);
for(int i=mid+1;i<=r;i++)
{
while(x<=mid&&a[x].x<=a[i].x)
{
if(a[x].op==0)dijah(a[x].y,1);
x++;
}
a[i].v+=gaia(a[i].y);
}
for(int i=l;i<=mid;i++)
{
if(a[i].op==0)clear(a[i].y);
}
if(l==1&&r==3*n)return;
x=l,y=mid+1,z=1;
while(x<=mid&&y<=r)
{
if(a[x].x>a[y].x)b[z]=a[y],y++,z++;
else b[z]=a[x],x++,z++;
}
while(y<=r)b[z]=a[y],y++,z++;
while(x<=mid)b[z]=a[x],x++,z++;
for(int i=l;i<=r;i++)a[i]=b[i-l+1];
return;
}
int main()
{
int x,y,g1=0;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
t[x].push_back(y);
t[y].push_back(x);
}
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
g.insert(a[i].y);
}
set<int>::iterator q=g.begin();
for(;q!=g.end();q++)
{
p[*q]=++g1;
}
for(int i=1;i<=n;i++)a[i].y=p[a[i].y];
dfs(1,0);
for(int i=1;i<=n;i++)a[i].p=dfn[i];
m=n;
for(int i=1;i<=n;i++)
{
a[++m].p=dfn[i];
a[m].op=2*i-1;
a[m].x=a[i].x;
a[m].y=a[i].y;
a[++m].p=out[i];
a[m].op=2*i;
a[m].x=a[i].x;
a[m].y=a[i].y;
}
sort(a+1,a+m+1,cmp1);
cdq(1,m);
sort(a+1,a+m+1,cmp3);
for(int i=n+1;i<=m;i+=2)
{
if(a[i+1].v==a[i].v)continue;
printf("%d\n",a[i+1].v-a[i].v);
}
return 0;
}