[P3899 [湖南集训] 更为厉害]

P3899 [湖南集训] 更为厉害

题目描述

T 为一棵有根树,我们做如下的定义:

  • abT 中的两个不同节点。如果 ab 的祖先,那么称“ab 更为厉害”。
  • abT 中的两个不同节点。如果 ab 在树上的距离不超过某个给定常数 x,那么称“ ab 彼此彼此”。

给定一棵 n 个节点的有根树 T,节点的编号为 1n,根节点为 1 号节点。
你需要回答 q 个询问,询问给定两个整数 pk,问有多少个有序三元组 (a,b,c) 满足:

  1. a,b,cT 中三个不同的点,且 ap 号节点;
  2. ab 都比 c 更为厉害;
  3. ab 彼此彼此。这里彼此彼此中的常数为给定的 k

数据范围:

n3105

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 的子树内进行选取,也就是说,这部分的贡献是:

qS(p)|dis(p,q)[1,k]siz[q]1

我们不难想到用线段树合并来维护这个式子,我们以dep[x] 为下标 siz[x]1 为点权建立一颗权值线段树,然后向上合并,那么对于每个点 p 上能取到的贡献就是 dep[depp,depp+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;
}
posted @   liuboom  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示