LGP8575 [DTOI R2] 星之河 学习笔记
LGP8575 [DTOI R2] 星之河 学习笔记
题意简述
给定一棵以 \(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\) 的大小关系。
浙公网安备 33010602011771号