谈笑风生题解
谈笑风生题解
怎么说呢,这道题出得倒挺好,
首先明确只有在一条链上才会有贡献 ,
我们要分情况讨论:
1.b在a下:每一个b的子树(除b外)都会有贡献,所以我们对每一个深度的b都加上siz[x]-1,贡献为深度为[deep[a],deep[a]+k-1]的权值之和;
2.a在b下:每一个a的子树(除a外)都会有贡献,b可以在max(1,deep[a]-k+1)中选择,所以这种情况的贡献为min(k,deep[a])*(siz[a]-1)。
然后,每次dfs搜完一个子节点,线段树合并,修改,查询;
#include<bits/stdc++.h>
#define ll long long
#define lc son[x][0]
#define rc son[x][1]
using namespace std;
const int N=3e5+7,P=27;
int n,m,t1,t2,num=0,cnt=0,cnt2=0,head[N],head2[N],rt[N],siz[N],son[N*P][2];
ll t,p,w[N*P<<2],answer[N];
struct edge{int nxt,to,i;}e[N<<1],g[N];
inline void add(int u,int v){e[++cnt].nxt=head[u],e[cnt].to=v,head[u]=cnt;}
inline void add2(int u,int v,int w){g[++cnt2].nxt=head2[u],g[cnt2].to=v,g[cnt2].i=w,head2[u]=cnt2;}
inline int read(){
int T=0,F=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
return F*T;
}
void update(int l,int r,int p,int &x,ll y){
if(!x) x=++num;
w[x]+=y;
if(l==r) return;
int mid=l+r>>1;
if(p<=mid) update(l,mid,p,lc,y);
else update(mid+1,r,p,rc,y);
}
ll query(int l,int r,int p,int q,int x){
if(!x) return 0;
if(p<=l&&r<=q) return w[x];
int mid=l+r>>1; ll ans=0;
if(p<=mid) ans+=query(l,mid,p,q,lc);
if(q>mid) ans+=query(mid+1,r,p,q,rc);
return ans;
}
void merge(int &x,int y,int l,int r){
if(!x||!y){x=x+y; return;}
int mid=l+r>>1; w[x]+=w[y];
merge(lc,son[y][0],l,mid),merge(rc,son[y][1],mid+1,r);
}
void dfs(int x,int fa,int d){
siz[x]=1;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa)
dfs(e[i].to,x,d+1),siz[x]+=siz[e[i].to],merge(rt[x],rt[e[i].to],1,n);
update(1,n,d,rt[x],siz[x]-1),t=siz[x]-1;
for(int i=head2[x];i;i=g[i].nxt) p=min(d-1,g[i].to),answer[g[i].i]=query(1,n,d+1,g[i].to+d,rt[x])+p*t;
}
int main(){
n=read(),m=read();
for(int i=1;i<n;++i) t1=read(),t2=read(),add(t1,t2),add(t2,t1);
for(int i=1;i<=m;++i) t1=read(),t2=read(),add2(t1,t2,i);
dfs(1,0,1);
for(int i=1;i<=m;++i) printf("%lld\n",answer[i]);
return 0;
}