LGP8575 [DTOI R2] 星之河 学习笔记

LGP8575 [DTOI R2] 星之河 学习笔记

Luogu Link

题意简述

给定一棵以 \(1\) 为根,大小为 \(n\) 的树。每个结点 \(u\)\(a_u,b_u\) 两个权值。对于每个结点 \(u\) 求其子树内(不包括自身)有多少结点 \(v\) 满足 \(a_v\le a_u,b_v\le b_u\)

做法解析

众所周知子树位置关系可以转化为dfs序大小关系。所以题目也就是问对于每个 \(u\) 有多少 \(v\) 满足 \(a_j\le a_i,b_j\le b_i,c_i<c_j<c_i+siz_u\)

好了现在就是三维偏序板子了,对,对吗?怎么处理 \(c_i<c_j<c_i+siz_u\) 这一坨东西?板子里的 \(c_j\le c_i\) 相当于说合法的 \(c_j\) 取值在 \([0,c_i]\),而现在 \(c_j\) 合法区间变成了 \((c_i,c_i+siz_u)\)……那好办啊!我们改变树状数组询问的范围就好了!

一个需要注意的是,我们排序的第三关键字是按 \(dfn\) 序降序排序,而不是升序。这是因为能造成贡献的 \(i,j\) 满足 \(b_j\le b_i,c_j>c_i\)。注意了!

代码实现

#include <bits/stdc++.h>
using namespace std;
namespace obasic{
    typedef vector<int> vecint;
    template <typename _T>
    void readi(_T &x){
        _T k=1;x=0;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
        x*=k;return;
    }
    template <typename _T>
    void writi(_T x){
        if(x<0)putchar('-'),x=-x;
        if(x>9)writi(x/10);
        putchar(x%10+'0');
    }
};
using namespace obasic;
const int MaxN=2e5+5;
int N,X,Y,ans[MaxN];
vecint Tr[MaxN];
struct anob{int x,y,z,s,id;}A[MaxN];
bool cmpx(anob a,anob b){return a.x!=b.x?a.x<b.x:(a.y!=b.y?a.y<b.y:a.z>b.z);}
bool cmpy(anob a,anob b){return a.y!=b.y?a.y<b.y:(a.x!=b.x?a.x<b.x:a.z>b.z);}
void addudge(int u,int v){
    Tr[u].push_back(v);
    Tr[v].push_back(u);
}
int dfn[MaxN],dcnt,siz[MaxN];
void dfs(int u,int f){
    dfn[u]=++dcnt,siz[u]=1;
    for(int v : Tr[u]){
        if(v==f)continue;
        dfs(v,u);siz[u]+=siz[v];
    }
}
struct BinidTree{
    int n,t[MaxN];
    void init(int x){n=x,fill(t,t+n+1,0);}
    int lowbit(int x){return x&(-x);}
    void add(int p,int x){for(;p<=n;p+=lowbit(p))t[p]+=x;}
    int gts(int p){int res=0;for(;p;res+=t[p],p-=lowbit(p));return res;}
    int getsum(int l,int r){return gts(r)-gts(l-1);}
}BidTr;
void cdqdac(int l,int r){
    if(l==r)return;int mid=(l+r)>>1;
    cdqdac(l,mid),cdqdac(mid+1,r);
    sort(A+l,A+mid+1,cmpy),sort(A+mid+1,A+r+1,cmpy);
    int p=l,q=mid+1;for(;q<=r;q++){
        for(;p<=mid&&A[p].y<=A[q].y;p++){
            BidTr.add(A[p].z,1);
        }
        ans[A[q].id]+=BidTr.getsum(A[q].z+1,A[q].z+A[q].s-1);
    }
    for(int i=l;i<p;i++)BidTr.add(A[i].z,-1);
}
int main(){
    readi(N);BidTr.init(N);
    for(int i=1;i<N;i++){
        readi(X),readi(Y);
        addudge(X,Y);
    }
    dfs(1,0);
    for(int i=1;i<=N;i++)A[i].id=i,A[i].z=dfn[i],A[i].s=siz[i];
    for(int i=1;i<=N;i++)readi(A[i].x),readi(A[i].y);
    sort(A+1,A+N+1,cmpx);cdqdac(1,N);
    for(int i=1;i<=N;i++)if(ans[i])writi(ans[i]),puts("");
    return 0;
}

反思总结

子树位置关系可以转化为dfs序大小关系。
当你发现有一维的偏序问的区间组成有点复杂,考虑让这一维上树状数组,因为树状数组可以应付非 \([1,x]\) 的区间查询。
关键字升序还是降序取决且仅取决于 \(k_i\)\(k_j\) 的大小关系。

posted @ 2025-02-20 17:35  矞龙OrinLoong  阅读(10)  评论(0)    收藏  举报