洛谷 P3899 [談笑風生]

GreenDuck發表於2019-03-27

簡化題意

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