[P3899 [湖南集训] 更为厉害]
P3899 [湖南集训] 更为厉害
题目描述
设 \(\text T\) 为一棵有根树,我们做如下的定义:
- 设 \(a\) 和 \(b\) 为 \(\text T\) 中的两个不同节点。如果 \(a\) 是 \(b\) 的祖先,那么称“\(a\) 比 \(b\) 更为厉害”。
- 设 \(a\) 和 \(b\) 为 \(\text T\) 中的两个不同节点。如果 \(a\) 与 \(b\) 在树上的距离不超过某个给定常数 \(x\),那么称“ \(a\) 与 \(b\) 彼此彼此”。
给定一棵 \(n\) 个节点的有根树 \(\text T\),节点的编号为 \(1\) 到 \(n\),根节点为 \(1\) 号节点。
你需要回答 \(q\) 个询问,询问给定两个整数 \(p\) 和 \(k\),问有多少个有序三元组 \((a,b,c)\) 满足:
- \(a,b,c\) 为 \(\text T\) 中三个不同的点,且 \(a\) 为 \(p\) 号节点;
- \(a\) 和 \(b\) 都比 \(c\) 更为厉害;
- \(a\) 和 \(b\) 彼此彼此。这里彼此彼此中的常数为给定的 \(k\)。
数据范围:
\(n\le 3*10^5\)
Solution:
话说之前暑假的时候貌似就见过这题,当时它貌似叫“谈笑风生”。
事先声明:本文中的 \(dep\) 是在根节点的深度为1的基础上定义的,同时,我将使用 \(S(x)\) 表示点 x 的子树
首先我们来思考一下那些点会产生贡献:

拿样例来看,对于询问 (2,2):合法的三元组有 \((2,1,4)\)、 \((2,1,5)\) 和 \((2,4,5)\)
我们先重新描述一下贡献:我们发现三元组中的 \(a\) , \(b\) 并无区别,所以我们假设 (a,b,c) 的深度是单调递增的
不难看出其实我们可以把贡献拆为两部分:以点 \(p\) 做为三元组中的 \(b\) 的贡献和以点 \(p\) 作为 \(a\) 的贡献
我们先求以点 \(p\) 做为三元组中的 \(b\) 的贡献:
在这种情况下,(fa[p]->1) 路径上的所有点都可以作为 \(a\) ,\(p\) 的子树内的所有节点都可以作为 \(c\) ,因此,该部分的贡献为:
\(min(dep[p]-1,k)*(siz[p]-1)\)
然后对于以点 \(p\) 作为 \(a\) 的贡献:
假设我们已经在点 p 的子树内钦定了一个点 \(q\) 作为 \(b\) ,那么 \(c\) 就应该从 \(q\) 的子树内进行选取,也就是说,这部分的贡献是:
$$\sum_{q\in{S(p)}|dis(p,q)\in[1,k]} siz[q]-1$$
我们不难想到用线段树合并来维护这个式子,我们以\(dep[x]\) 为下标 \(siz[x]-1\) 为点权建立一颗权值线段树,然后向上合并,那么对于每个点 \(p\) 上能取到的贡献就是 \(dep\in[dep_p,dep_p+k]\) 这段区间的总和
然后说一些细节:
由于是线段树合并,所以如果想要在线做这道题的话在 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;
}

浙公网安备 33010602011771号