[bzoj3653]谈笑风生
发现a是c的祖先,b是c的祖先,则a和b也一定是祖先关系。
a已经确定了,考虑b:1.若a是b的祖先,考虑a中每一个节点作为c的方案数,显然就是min(d[x]-d[a]-1,k),其中x!=a且在a的子树中;2.若b是a的祖先,很好处理,即min(d[a],k)*(sz[a]-1)(深度从0开始)。
第一个式子可以转化为min(d[x],k+d[a]+1)-d[a]-1,然后对于dfs序,开一棵可持久化权值线段树(权值为深度),对于区间维护区间深度和和区间个数,然后询问分两部分计算即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 300005 4 #define ll long long 5 #define mid (l+r>>1) 6 struct ji{ 7 int nex,to; 8 }edge[N<<1]; 9 int V,E,n,m,x,y,head[N],r[N],sh[N],sz[N],id[N],ls[N*30],rs[N*30]; 10 ll ans,f[N*20][2]; 11 void add(int x,int y){ 12 edge[E].nex=head[x]; 13 edge[E].to=y; 14 head[x]=E++; 15 } 16 void update(int k1,int &k2,int l,int r,int x){ 17 k2=++V; 18 f[k2][0]=f[k1][0]+1; 19 f[k2][1]=f[k1][1]+x; 20 if (l==r)return; 21 ls[k2]=ls[k1]; 22 rs[k2]=rs[k1]; 23 if (x<=mid)update(ls[k1],ls[k2],l,mid,x); 24 else update(rs[k1],rs[k2],mid+1,r,x); 25 } 26 ll query(int k1,int k2,int l,int r,int x,int y,int p){ 27 if ((l>y)||(x>r)||(!k2))return 0; 28 if ((x<=l)&&(r<=y))return f[k2][p]-f[k1][p]; 29 return query(ls[k1],ls[k2],l,mid,x,y,p)+query(rs[k1],rs[k2],mid+1,r,x,y,p); 30 } 31 void dfs(int k,int fa,int s){ 32 id[k]=++x; 33 if (x>1)update(r[x-1],r[x],1,n,s); 34 sh[k]=s; 35 sz[k]=1; 36 for(int i=head[k];i!=-1;i=edge[i].nex) 37 if (edge[i].to!=fa){ 38 dfs(edge[i].to,k,s+1); 39 sz[k]+=sz[edge[i].to]; 40 } 41 } 42 int main(){ 43 V=r[1]=1; 44 scanf("%d%d",&n,&m); 45 memset(head,-1,sizeof(head)); 46 for(int i=1;i<n;i++){ 47 scanf("%d%d",&x,&y); 48 add(x,y); 49 add(y,x); 50 } 51 x=0; 52 dfs(1,0,0); 53 for(int i=1;i<=m;i++){ 54 scanf("%d%d",&x,&y); 55 ans=1LL*(sz[x]-1)*min(-1,y-sh[x]-1); 56 y+=sh[x]+1; 57 ans+=query(r[id[x]],r[id[x]+sz[x]-1],1,n,1,min(n,y),1); 58 if (y<n)ans+=query(r[id[x]],r[id[x]+sz[x]-1],1,n,y+1,n,0)*y; 59 printf("%lld\n",ans); 60 } 61 }