C67 线段树合并 P3899 [湖南集训] 更为厉害
视频链接:253 线段树合并 P3899 [湖南集训] 更为厉害_哔哩哔哩_bilibili
C64 可持久化线段树+DFS P3899 [湖南集训] 更为厉害 - 董晓 - 博客园 (cnblogs.com)
#include <iostream> #include <cstring> #include <algorithm> #include <vector> using namespace std; void read(int &x){ //快读 x=0; char c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c))x=x*10+c-'0',c=getchar(); } #define LL long long #define mid (l+r)/2 #define N 300005 int n,m; vector<int>g[N]; //邻接表 int dep[N],siz[N]; //树信息 int root[N],tot; //根节点,开点个数 int ls[N*40],rs[N*40];LL sum[N*40]; //sum:权值和 void change(int &u,int l,int r,int p,int v){ //点修 if(!u)u=++tot; //开点 if(l==r){sum[u]+=v; return;} if(p<=mid)change(ls[u],l,mid,p,v); else change(rs[u],mid+1,r,p,v); sum[u]=sum[ls[u]]+sum[rs[u]]; } int merge(int x,int y){ //合并 if(!x||!y)return x+y; int cur=++tot; //开点 sum[cur]=sum[x]+sum[y]; ls[cur]=merge(ls[x],ls[y]); rs[cur]=merge(rs[x],rs[y]); return cur; } void dfs(int x,int f){ //递归合并 dep[x]=dep[f]+1; siz[x]=1; for(int y:g[x]){ if(y==f)continue; dfs(y,x); siz[x]+=siz[y]; root[x]=merge(root[x],root[y]); } change(root[x],1,n,dep[x],siz[x]-1); } LL query(int u,int l,int r,int x,int y){ //区查 if(x<=l&&r<=y) return sum[u]; LL s=0; if(x<=mid)s+=query(ls[u],l,mid,x,y); if(y>mid)s+=query(rs[u],mid+1,r,x,y); return s; } int main(){ read(n),read(m); for(int i=1,x,y; i<n; i++){ read(x),read(y); g[x].push_back(y);g[y].push_back(x); } dfs(1,0); for(int i=1,a,k; i<=m; i++){ read(a),read(k); LL ans=1ll*min(k,dep[a]-1)*(siz[a]-1) +query(root[a],1,n,dep[a]+1,min(dep[a]+k,n)); printf("%lld\n",ans); } }