【题解 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;
}
posted @ 2024-01-24 16:25  dijah  阅读(20)  评论(0编辑  收藏  举报