[P3899 [湖南集训] 更为厉害]
P3899 [湖南集训] 更为厉害
题目描述
设
- 设
和 为 中的两个不同节点。如果 是 的祖先,那么称“ 比 更为厉害”。 - 设
和 为 中的两个不同节点。如果 与 在树上的距离不超过某个给定常数 ,那么称“ 与 彼此彼此”。
给定一棵
你需要回答
为 中三个不同的点,且 为 号节点; 和 都比 更为厉害; 和 彼此彼此。这里彼此彼此中的常数为给定的 。
数据范围:
Solution:
话说之前暑假的时候貌似就见过这题,当时它貌似叫“谈笑风生”。
事先声明:本文中的
首先我们来思考一下那些点会产生贡献:
拿样例来看,对于询问 (2,2):合法的三元组有
我们先重新描述一下贡献:我们发现三元组中的
不难看出其实我们可以把贡献拆为两部分:以点
我们先求以点
在这种情况下,(fa[p]->1) 路径上的所有点都可以作为
然后对于以点
假设我们已经在点 p 的子树内钦定了一个点
我们不难想到用线段树合并来维护这个式子,我们以
然后说一些细节:
由于是线段树合并,所以如果想要在线做这道题的话在 merge 的时候一定要新建一个节点作为合并的结果,否则就会使得一个点上挂有同层其他节点的贡献,这样显然是错误的
但是我们发现本题可以离线,所以我们将每个询问挂到点上,我们在构建完一个点的线段树后直接查询,在其他节点乱入之前完成答案统计。
Code:
#include<bits/stdc++.h> #define ll long long const int N=6e5+5; using namespace std; struct Segment_Tree{ int rt[N],cnt; struct Tree{ int ls,rs; ll cnt; }t[N*40]; void pushup(int x){t[x].cnt=t[t[x].ls].cnt+t[t[x].rs].cnt;} void insert(int &x,int l,int r,int pos,ll val) { t[x= (x? x : ++cnt)].cnt+=val; if(l==r)return; int mid=l+r>>1; if(pos<=mid)insert(t[x].ls,l,mid,pos,val); if(mid<pos) insert(t[x].rs,mid+1,r,pos,val); } int merge(int x,int y,int l,int r) { if(!x||!y)return x|y; if(l==r){t[x].cnt+=t[y].cnt;return x;} int mid=l+r>>1; t[x].ls=merge(t[x].ls,t[y].ls,l,mid); t[x].rs=merge(t[x].rs,t[y].rs,mid+1,r); pushup(x); return x; } ll query(int x,int l,int r,int L,int R) { if(!x)return 0; if(L<=l&&r<=R){return t[x].cnt;} int mid=l+r>>1;ll res=0; if(L<=mid)res+=query(t[x].ls,l,mid,L,R); if(mid<R) res+=query(t[x].rs,mid+1,r,L,R); return res; } }T; int n,m; vector<int>E[N]; int dep[N],st[N],ed[N],siz[N]; vector<tuple<int,int> > Q[N]; ll ans[N]; struct Graph{ void dfs(int x,int fa) { dep[x]=dep[fa]+1;siz[x]=1; for(auto y : E[x]) { if(y==fa)continue; dfs(y,x); siz[x]+=siz[y]; } T.insert(T.rt[x],1,n,dep[x],siz[x]-1); for(auto y : E[x]){if(y==fa)continue;T.rt[x]=T.merge(T.rt[x],T.rt[y],1,n);} for(auto [k,id] : Q[x]) { ans[id]=T.query(T.rt[x],1,n,dep[x]+1,dep[x]+k)+(1ll*siz[x]-1)*1ll*min(1ll*dep[x]-1,1ll*k); } } }G; void work() { cin>>n>>m; for(int i=1,x,y;i<n;i++){cin>>x>>y;E[x].push_back(y);E[y].push_back(x);} for(int i=1,x,k;i<=m;i++) { cin>>x>>k; Q[x].emplace_back(k,i); } G.dfs(1,0); for(int i=1;i<=m;i++)cout<<ans[i]<<"\n"; } int main() { //freopen("P3899.in","r",stdin);freopen("P3899.out","w",stdout); ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0); work(); return 0; }