CDQ分治 星之河

「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\)

原题链接

首先蓝星和红星的限制是一个二位偏序,但是与常规的CDQ分治的三维偏序不同的是,这个问题的第三维的限制不是线性的(比如时间戳),而是树上结构。
那就考虑转化

把这颗树,按照dfs序,变成一个数组,每一个点在这个数组里面的下表就是它的第三维度,我们要求每一颗子树,对应的就是一个区间,那只需要把每一个变成两个询问,然后把这两个询问的结果相减,就是答案。

。。我第一次写的时候只记录了一下每一个点的子树在dfn序的开始和结尾的位置,然后答案就直接用这个相减。。
很明显。。错了,因为dfn序的开头处自然是这个点,但是结尾处的点的信息和这个点不一样,相减得到的结果没有意义
需要往结尾处再插入一个和这个点前两个信息一样,但是dfn序的信息和结尾处点的dfn一样的点,然后为了防止重复的麻烦,给这个点打一个标记,防止影响后面的点的答案

写起来还是有点烦吧

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read() {
	char c=getchar();ll a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
struct edge
{
	ll next,to;
}e[400001];
ll head[400001],tot,n,a[400001],b[400001],c[400001],dfn,tr[400001],p[400001],st[400001],ed[400001];
ll cnt[400001],tem[400001],ned[400001];
inline ll lowbit(ll x)
{
	return x&(-x);
}
inline void add(ll i,ll x)
{
	while(i<=n)
	{
		tr[i]+=x;
		i+=lowbit(i);
	}
}
inline ll ask(ll i)
{
	ll ans=0;
	while(i>0)
	{
		ans+=tr[i];
		i-=lowbit(i);
	}
	return ans;
}
inline void ad1(ll i,ll j)
{
	e[++tot].next=head[i];
	e[tot].to=j;
	head[i]=tot;
}
void dfs(ll x,ll fa)
{
	c[x]=++dfn;
	st[x]=dfn;
	for(ll i=head[x];i!=0;i=e[i].next)
	{
		ll u=e[i].to;
		if(u==fa)continue;
		dfs(u,x);
	}
	ed[x]=dfn;
}
bool mycmp1(ll e,ll f)
{
	if(a[e]==a[f]&&b[e]==b[f]&&c[e]==c[f])return ned[e]>ned[f];
	if(a[e]==a[f]&&b[e]==b[f])return c[e]<c[f];
	if(a[e]==a[f])return b[e]<b[f];
	return a[e]<a[f];
}
void cdq(ll l,ll r)
{
	if(l==r)return;
	ll mid=l+r>>1;
	cdq(l,mid);cdq(mid+1,r);
	ll j=mid+1,i=l,k=l;
	while(j<=r)
	{
		while(i<=mid&&b[p[i]]<=b[p[j]])
		{
			if(ned[p[i]]==1)add(c[p[i]],1);
			tem[k++]=p[i];
			i++;
		}
		cnt[p[j]]+=ask(c[p[j]]);
		tem[k++]=p[j];
		j++;
	}
	for(ll h=l;h<i;h++)if(ned[p[h]])add(c[p[h]],-1);
	while(i<=mid)tem[k++]=p[i++];
	for(ll i=l;i<=r;i++)
	{
		p[i]=tem[i];
	}
}
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n=read();
	for(ll i=1;i<n;i++)
	{
		ll a=read(),b=read();
		ad1(a,b);ad1(b,a);
	}
	dfs(1,0);
	for(ll i=1;i<=n;i++)
	{
		a[i]=read(),b[i]=read();
		p[i]=i;ned[i]=1;
	}
	for(ll i=1;i<=n;i++)
	{
		p[i+n]=i+n;
		a[i+n]=a[i];
		b[i+n]=b[i];
		c[i+n]=ed[i];
		ned[i+n]=0;
	}
	sort(p+1,p+1+n+n,mycmp1);
	cdq(1,n+n);
	sort(p+1,p+1+n+n);
	for(ll i=1;i<=n;i++)
	{
		ll ans=cnt[p[i+n]]-cnt[p[i]]-1;
		if(ans!=0)cout<<ans<<endl;
	}
	return 0;
}
posted @ 2024-01-12 13:40  HL_ZZP  阅读(4)  评论(0编辑  收藏  举报