洛谷 P3899 [谈笑风生]

简化题意

m次询问,每次询问x的子树中,与x节点距离不超过y的节点的子树和。n,m≤300,000。


 

思路

按照dfs序排序,每次将一个点的答案塞到第depu的位置,这样得到一个前缀和,每次询问作减法即可。

可持久化线段树。


 

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=3E5+5;
 4 typedef long long int ll;
 5 ll n,m,x,y,sum[maxn],dep[maxn],cur,t[maxn*20],root[maxn*20],fa[maxn],size,gg;
 6 ll ls[maxn*20],rs[maxn*20],ti,dfn[maxn],last[maxn],maxx[maxn],head[maxn*2];
 7 struct edge{ll to,next;}E[maxn*2];
 8 void insert(ll l,ll r,ll pos,ll&num,ll pre,ll val)
 9 {
10     num=++cur;
11     t[num]=t[pre];
12     ls[num]=ls[pre];
13     rs[num]=rs[pre];
14     t[num]+=val;
15     if(l==r)return;
16     ll mid=(l+r)>>1;
17     if(pos<=mid)insert(l,mid,pos,ls[num],ls[pre],val);
18     else insert(mid+1,r,pos,rs[num],rs[pre],val);
19 }
20 ll ask(ll L,ll R,ll l,ll r,ll num)
21 {
22     if(L<=l&&r<=R)return t[num];
23     else if(r<L||R<l)return 0;
24     ll mid=(l+r)>>1;
25     return ask(L,R,l,mid,ls[num])+ask(L,R,mid+1,r,rs[num]);
26 }
27 void add(ll u,ll v)
28 {
29     E[++size].to=v;
30     E[size].next=head[u];
31     head[u]=size;
32 }
33 void dfs(ll u,ll F,ll d)
34 {
35     dep[u]=d;
36     fa[u]=F;
37     sum[u]=1;
38     maxx[u]=dfn[u]=++ti;
39     last[u]=u;
40     for(ll i=head[u];i;i=E[i].next)
41     {
42         ll v=E[i].to;
43         if(v==F)continue;
44         dfs(v,u,d+1);
45         sum[u]+=sum[v];
46         if(maxx[v]>maxx[u])
47         {
48             maxx[u]=maxx[v];
49             last[u]=last[v];
50         }
51     }
52 }
53 void init(ll u,ll F,ll d)
54 {
55     insert(1,n,d,root[u],root[gg],sum[u]-1);
56     gg=u;
57     for(ll i=head[u];i;i=E[i].next)
58     {
59         ll v=E[i].to;
60         if(v==F)continue;
61         init(v,u,d+1);
62     }
63 }
64 int main()
65 {
66 //    freopen("laugh.in","r",stdin);
67 //    freopen("laugh.out","w",stdout);
68     ios::sync_with_stdio(false);
69     cin>>n>>m;
70     for(ll i=1;i<=n-1;++i)
71     {
72         cin>>x>>y;
73         add(x,y);
74         add(y,x);
75     }
76     dfs(1,1,1);
77     gg=1;
78     init(1,1,1);
79     for(ll i=1;i<=m;++i)
80     {
81         cin>>x>>y;
82         if(x==1)cout<<ask(2,1+y,1,n,root[last[1]])<<endl;
83         else cout<<(sum[x]-1)*min(dep[x]-1,y)+
84         ask(dep[x]+1,dep[x]+y,1,n,root[last[x]])-ask(dep[x]+1,dep[x]+y,1,n,root[x])<<endl;
85     }
86     return 0;
87 }
View Code

 

转载于:https://www.cnblogs.com/GreenDuck/p/10610636.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值