题意:
设 T 为一棵有根树,我们做如下的定义:
• 设 a 和 b 为 T 中的两个不同节点。如果 a 是 b 的祖先,那么称“a 比 b 不知道高明到哪里去了”。
• 设 a 和 b 为 T 中的两个不同节点。如果 a 与 b 在树上的距离不超过某个给定常数 x,那么称“a 与 b 谈笑风生”。
给定一棵 n 个节点的有根树 T,节点的编号为 1 ∼ n,根节点为 1 号节点。你需要回答 q 个询问,询问给定两个整数 p 和 k,问有多少个有序三元组 (a; b; c) 满足:
1. a、 b 和 c 为 T 中三个不同的点,且 a 为 p 号节点;
2. a 和 b 都比 c 不知道高明到哪里去了;
3. a 和 b 谈笑风生。这里谈笑风生中的常数为给定的 k。
分析:
这道题目,也许是出题人有意地把题目描述复杂化了。实际上这并不是一道难题,甚至达不到近年来省选的难度。
但是这确实是一道值得交流的题目,我见过的这道题就有不下五种做法,有在线的,有离线的,我用的是在线的主席树维护dfs序的方法。我开始以为这道题让求的是这样的有序三元组的数目,感觉十分不可做,后来发现,a点是已经固定的。
我们不考虑什么“高明到哪里去了”,三元组的要求是说,给出a,求点a和点b都在c到根的路径上,也就是都是c的祖先,并且a和b距离不超过k的(a,b)数目。
因为a和b在c的父链上这个限制,我们可以分类讨论,如果b也在a的父链上,那么应该很好求,就是这条链上深度和a相差不超过k的点数目,乘上(a的子树size-1)(减一是因为a自己不算),这就是一个简单的乘法原理,预处理了O(1)就能求出。
然后是假如b本身就存在于a的一个子树中,距离不超过k,我们知道,一棵子树,在dfs序上是连续的,所以我们在预处理是,以dfs序为版本,以深度为区间,以子树大小为权值建主席树(原因是因为此时我只需要控制深度差范围内的所有贡献,并不一定要知道是具体哪个点的贡献,所以深度一样我们通通加到一个主席树节点权值里)。
查询?很好说了吧,只要我们知道深度差的范围和这个点对应的子树dfs序区段。
代码:
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int N=300005,inf=1e9; 5 struct node{int y,nxt;}e[N*2]; 6 int n,m,md,h[N],c=0,d[N],sz[N]; 7 int rt[N],cnt,tot,dfn[N],fa[N]; 8 struct ch{int l,r;ll da;}t[N*30]; 9 void add(int x,int y){ 10 e[++c]=(node){y,h[x]};h[x]=c; 11 e[++c]=(node){x,h[y]};h[y]=c; 12 } void dfs1(int x){sz[x]=1; 13 for(int i=h[x],y;i;i=e[i].nxt) 14 if(!d[y=e[i].y]){ 15 d[y]=d[x]+1;md=max(md,d[y]); 16 fa[y]=x;dfs1(y);sz[x]+=sz[y]; 17 } return ; 18 } void update(int &x,int l,int r,ll v,int p){ 19 t[++cnt]=t[x];x=cnt;t[x].da+=v; 20 if(l==r) return;int mid=l+r>>1; 21 if(p<=mid) update(t[x].l,l,mid,v,p); 22 else update(t[x].r,mid+1,r,v,p); 23 } ll query(int x,int y,int l,int r,int L,int R){ 24 if(L<=l&&r<=R) return t[y].da-t[x].da; 25 int mid=l+r>>1;ll re=0; 26 if(L<=mid)re+=query(t[x].l,t[y].l,l,mid,L,R); 27 if(mid<R)re+=query(t[x].r,t[y].r,mid+1,r,L,R); 28 return re; 29 } void dfs2(int x){ 30 dfn[x]=++tot;rt[tot]=rt[tot-1]; 31 update(rt[tot],1,md,sz[x]-1,d[x]); 32 for(int i=h[x],y;i;i=e[i].nxt){ 33 if((y=e[i].y)==fa[x]) continue; 34 dfs2(y); 35 } return ; 36 } int main(){ 37 scanf("%d%d",&n,&m); 38 for(int i=1,x,y;i<n;i++) 39 scanf("%d%d",&x,&y),add(x,y); 40 d[1]=1;dfs1(1);dfs2(1); 41 for(int i=1;i<=m;i++){ 42 int p,k;scanf("%d%d",&p,&k); 43 ll ans=(sz[p]-1)*1ll*(min(k,d[p]-1)); 44 int l=dfn[p]-1,r=dfn[p]+sz[p]-1; 45 if(d[p]==md){puts("0");continue;} 46 int z=min(md,d[p]+k); 47 ans+=query(rt[l],rt[r],1,md,d[p]+1,z); 48 printf("%lld ",ans); 49 } return 0; 50 }