传送门
题意:
一棵树,每次给k个控制点,求每个控制点能控制几个点。(一个点被离它最近的控制点控制)。
首先要dp出离每个虚树上的点最近的点。
dp两次即可。一次找子树,一次找父亲。
其次,对于虚树的根节点,根节点以外的其他所有不在虚树中的点被离虚树中的根节点最近的点控制。
对于虚树中的任意一条路径,倍增求出原树种这条路径上离现在的点最近的点。如果这条路径两边从属一样,那么直接加上即可。
如果不一样,需要倍增查找分界点(一半从属下面,一半从属上面)。分别累加。
对于不在虚树中的子树,大小为该点的子树大小减去虚树中最近点的子树大小。
- Code
#include<bits/stdc++.h>
using namespace std;
namespace IO
{
streambuf *ib,*ob;int buf[50];
inline void init()
{
ios::sync_with_stdio(false);
cin.tie(NULL);cout.tie(NULL);
ib=cin.rdbuf();ob=cout.rdbuf();
}
inline int read()
{
char ch=ib->sbumpc();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=ib->sbumpc();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=ib->sbumpc();}
return i*f;
}
inline void W(int x)
{
if(!x){ob->sputc('0');return;}
if(x<0){ob->sputc('-');x=-x;}
while(x){buf[++buf[0]]=x%10;x/=10;}
while(buf[0])ob->sputc(buf[buf[0]--]+'0');
}
}
const int Maxn=3e5+50;
int n,m,rt,q,vt,dfn[Maxn],ind,dep[Maxn],sze[Maxn],fa[Maxn][20],vir[Maxn],id[Maxn],dis[Maxn],belong[Maxn],ans[Maxn];
vector<int>edge[Maxn];
vector<int>nowedge[Maxn];
inline int getlca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
if(dep[x]!=dep[y])
{
int b=dep[x]-dep[y];
for(int i=0;i<=18&&b;i++)
{
if(b&1)x=fa[x][i];
b>>=1;
}
}
if(x==y)return x;
for(int i=18;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
}
return fa[x][0];
}
inline void dfs(int now,int f)
{
dep[now]=dep[f]+1;sze[now]=1;fa[now][0]=f;dfn[now]=++ind;
for(int i=1;i<=18;i++)fa[now][i]=fa[fa[now][i-1]][i-1];
for(int e=edge[now].size()-1;e>=0;e--)
{
int v=edge[now][e];
if(v==f)continue;
dfs(v,now);sze[now]+=sze[v];
}
}
inline bool compdfn(const int &a,const int &b){return dfn[a]<dfn[b];}
inline void BuildAuxTree()
{
static int a[Maxn],nowfa[Maxn],cnt,sta[Maxn],top;
cnt=m=IO::read();++vt;top=0;
for(int i=1;i<=m;i++)a[i]=vir[i]=IO::read(),id[vir[i]]=vt,ans[a[i]]=0;
sort(a+1,a+m+1,compdfn);
for(int i=1;i<=m;i++)
{
if(!top)
{
rt=a[i];
nowfa[a[i]]=0;
sta[++top]=a[i];
continue;
}
int lca=getlca(a[i],sta[top]);
if(lca!=sta[top])
{
while(dep[sta[top]]>dep[lca])
{
if(dep[sta[top-1]]<dep[lca])nowfa[sta[top]]=lca;
top--;
}
if(lca!=sta[top])
{
if(!top)rt=lca;
a[++cnt]=lca;
nowfa[lca]=sta[top];
sta[++top]=lca;
}
}
nowfa[a[i]]=sta[top];
sta[++top]=a[i];
}
for(int i=1;i<=cnt;i++)nowedge[a[i]].clear();
for(int i=1;i<=cnt;i++)nowedge[nowfa[a[i]]].push_back(a[i]);
}
inline void FindClose1(int now,int f)
{
if(id[now]==vt)dis[now]=0,belong[now]=now;
else dis[now]=n+1,belong[now]=n+1;
for(int e=nowedge[now].size()-1;e>=0;e--)
{
int v=nowedge[now][e];
if(v==f)continue;
FindClose1(v,now);
int len=dep[v]-dep[now];
if(dis[now]>dis[v]+len)dis[now]=dis[v]+len,belong[now]=belong[v];
else if(dis[now]==dis[v]+len&&belong[now]>belong[v])belong[now]=belong[v];
}
}
inline void FindClose2(int now,int f,int dist)
{
if(now!=rt)
{
if(dis[f]+dist<dis[now])dis[now]=dis[f]+dist,belong[now]=belong[f];
else if(dis[f]+dist==dis[now]&&belong[now]>belong[f])belong[now]=belong[f];
}
for(int e=nowedge[now].size()-1;e>=0;e--)
{
int v=nowedge[now][e];
if(v==f)continue;
FindClose2(v,now,dep[v]-dep[now]);
}
}
inline int GetClosest(int x,int y)
{
for(int i=18;i>=0;i--)
if(dep[fa[x][i]]>dep[y])x=fa[x][i];
return x;
}
inline void GetAns(int now,int f)
{
if(now==rt)ans[belong[now]]+=sze[1]-sze[now];
int w=sze[now];
for(int e=nowedge[now].size()-1;e>=0;e--)
{
int v=nowedge[now][e];
if(v==f)continue;
int suf=GetClosest(v,now);
w-=sze[suf];
if(belong[v]==belong[now])ans[belong[now]]+=(sze[suf]-sze[v]);
else
{
int suf2=v;
for(int i=18;i>=0;i--)
{
if(dep[fa[suf2][i]]>dep[now])
{
if(dep[v]-dep[fa[suf2][i]]+dis[v]<dep[fa[suf2][i]]-dep[now]+dis[now])suf2=fa[suf2][i];
else if((dep[v]-dep[fa[suf2][i]]+dis[v]==dep[fa[suf2][i]]-dep[now]+dis[now])&&(belong[v]<belong[now]))suf2=fa[suf2][i];
}
}
ans[belong[now]]+=sze[suf]-sze[suf2];
ans[belong[v]]+=sze[suf2]-sze[v];
}
GetAns(v,now);
}
ans[belong[now]]+=w;
}
int main()
{
IO::init();n=IO::read();
for(int i=1;i<n;i++)
{
int x=IO::read(),y=IO::read();
edge[x].push_back(y);edge[y].push_back(x);
}
dfs(1,0);
q=IO::read();
while(q--)
{
BuildAuxTree();
FindClose1(rt,0);
FindClose2(rt,0,0);
GetAns(rt,0);
for(int i=1;i<=m;i++)IO::W(ans[vir[i]]),IO::ob->sputc(' ');
IO::ob->sputc('\n');
}
}