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;
}