P6626 [省选联考 2020 B 卷] 消息传递
简要题意
给你一个
本题解法:点分治
首先我们需要把询问挂上树,离线处理,给每个点开个 vector 存下来。
由于是求点的个数,所以分两种情况讨论:在没有将这个询问所在的点作为当前点分治的中心时,我们要将所有询问中
实际情况中我们是不分类的,那样会增大码量,统一使用桶记录该距离有多少个点解决。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int T,n,m,ver[N*2],ne[N*2],head[N],tot,root,siz[N],son[N],ma,s,ans[N],dep[N],t[N];
bool vis[N];
void add(int x,int y)
{
ver[++tot]=y;ne[tot]=head[x];head[x]=tot;
}
vector<pair<int,int> > v[N],q;
void getroot(int x,int fa)
{
siz[x]=1;son[x]=0;
for(int i=head[x];i;i=ne[i])
{
int y=ver[i];
if(y==fa || vis[y]) continue;
getroot(y,x);
siz[x]+=siz[y];
son[x]=max(son[x],siz[y]);
}
son[x]=max(son[x],s-siz[x]);
if(son[x]<ma) ma=son[x],root=x;
}
int md;
void getdis(int x,int fa)
{
t[dep[x]]++;md=max(md,dep[x]);
for(auto i : v[x])
{
if(i.first+1<dep[x]) continue;
q.push_back(make_pair(i.first-dep[x]+2,i.second));
}
for(int i=head[x];i;i=ne[i])
{
int y=ver[i];
if(y==fa || vis[y]) continue;
dep[y]=dep[x]+1;
getdis(y,x);
}
}
void consolate(int x)
{
q.clear();md=0;
dep[x]=1;getdis(x,0);
for(auto i : q)
{
ans[i.second]+=t[i.first];
}
for(int i=1;i<=md;i++) t[i]=0;
for(int i=head[x];i;i=ne[i])
{
int y=ver[i];
if(vis[y]) continue;
md=0;q.clear();
dep[y]=2;getdis(y,x);
for(auto j : q)
{
ans[j.second]-=t[j.first];
}
for(int j=1;j<=md;j++) t[j]=0;
}
}
void divide(int rt)
{
consolate(rt);
vis[rt]=1;
for(int i=head[rt];i;i=ne[i])
{
int y=ver[i];
if(vis[y]) continue;
ma=0x3f3f3f3f;s=siz[y];root=0;
getroot(y,0);
divide(root);
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
memset(head,0,sizeof(head));
memset(ans,0,sizeof(ans));
memset(vis,0,sizeof(vis));tot=0;
for(int i=1;i<=n;i++) v[i].clear();
for(int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
v[x].push_back(make_pair(y,i));
}
ma=0x3f3f3f3f,s=n;
getroot(1,0);
divide(root);
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
return 0;
}