【题解 P8575】 星之河

「DTOI-2」星之河

题目背景

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

题目描述

星之统治者有一个星盘,其可以被抽象为一棵根节点为 1 的树。树上每个节点 i 有一颗红星、一颗蓝星,亮度分别记为 Redi,Bluei

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

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

输入格式

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

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

接下来 n 行每行两个整数分别表示 Redi,Bluei

输出格式

每个答案非 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
对于节点 510,没有小于等于他的子节点,因此不输出。

数据范围

Subtask n 特殊性质 总分数
1 1000 10
2 5×104 20
3 105 200Redi,Bluei200 20
4 2×105 树的形态是链 20
5 2×105 30

对于所有数据,保证 n2×105109Redi,Bluei109

解题思路

此题有些偏模板。
分析题意,就是找每个节点的子树中有多少个节点 redblue 都小于自己。
利用欧拉序给每个节点编号,那么一个节点 x 的子树范围就是 dfnx+1outx ,不包获该节点。
可以转化为 1dfnx+1outx 中,满足要求的有多少个,相减即可。
多个询问,每次求节点号,redblue 都小于自己的有多少个节点,三维偏序问题。
CDQ 分治解决,先在分治外确定完一维,分治内部合并时排序第二维,最后一维用树状数组即可。
时间复杂度 O(nlog2n)

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 @   dijah  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示