- 题意:[HNOI2014]世界树
- 思路:
首先建好虚树。(为了方便size的预处理,强制虚数里面的根为1)
预处理出每个点最近的关键点bl,以及到它的距离。
然后一条边上(不含两端):(u->v)
且设p为u的儿子中是v的祖先的。
1.bl[u]=bl[v]: ans[bl[u]]=sz[p]−sz[v];
2.bl[u]!=bl[v]: 一定存在一个点为bl[u]和bl[v]的分割点。倍增找就行了。找到点x。
ans[bl[v]]+=sz[x]−sz[v]
ans[bl[u]]+=sz[p]−sz[x]
端点上的点:每个点要算的是它的所有满足子树内没有关键点的非虚数上的节点。
很简单,我们容斥一下,用sz[u]减去所有的sz[p]记得多个v可能有相同的p要标记去重。
- code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+5;
int tt[N],h[N],ans[N],sz[N],inf=1e9,dis[N],bl[N],st[N],tp,In[N],Out[N],Time,fa[N][21],nxt[N<<1],to[N<<1],head[N],dep[N],ecnt,lg[N],mark[N],dp[N],len[N<<1];
bool vis[N];
struct node {int p,dfn;}a[N];
bool cmp(node u,node v) {return u.dfn<v.dfn;}
void add_edge(int u,int v,ll w) {nxt[++ecnt]=head[u];to[ecnt]=v;len[ecnt]=w;head[u]=ecnt;}
void init(int u) {
In[u]=++Time;sz[u]=1;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(v==fa[u][0])continue;
fa[v][0]=u,dep[v]=dep[u]+1;
for(int j=1;j<=lg[dep[v]];j++) fa[v][j]=fa[fa[v][j-1]][j-1];
init(v);sz[u]+=sz[v];
}
Out[u]=Time;
}
int Lca(int u,int v) {
if(dep[u]<dep[v]) swap(u,v);
int k=dep[u]-dep[v];
for(int i=0;i<=lg[k];i++) if((1<<i)&k)u=fa[u][i];
if(u==v)return u;
for(int i=lg[dep[u]];i>=0;i--)
if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
void dfs1(int u) {
if(mark[u]==1) dis[u]=0,bl[u]=u;
else dis[u]=inf;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
dfs1(v);
if(mark[u]!=1) {
if(dis[v]+len[i]<dis[u]) dis[u]=dis[v]+len[i],bl[u]=bl[v];
else if(dis[v]+len[i]==dis[u]&&bl[u]>bl[v]) bl[u]=bl[v];
}
}
}
void dfs2(int u) {
ans[u]=0;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(mark[v]==2) {
if(dis[u]+len[i]<dis[v]) dis[v]=dis[u]+len[i],bl[v]=bl[u];
else if(dis[u]+len[i]==dis[v]&&bl[v]>bl[u]) bl[v]=bl[u];
}
dfs2(v);
}
}
void solve(int u) {
ans[bl[u]]+=sz[u];
int cc=0;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i],p=v,k=dep[v]-dep[u]-1;
for(int j=0;j<=lg[k];j++)if((1<<j)&k)p=fa[p][j];
if(!vis[p])ans[bl[u]]-=sz[p],vis[p]=1,tt[++cc]=p;
if(bl[u]==bl[v]) {ans[bl[u]]+=sz[p]-sz[v];continue;}
int x=v,tmp=dis[v]-dis[u]+dep[v]+dep[u];
for(int j=lg[k];j>=0;j--) {
if(tmp<dep[fa[x][j]]*2||(tmp==dep[fa[x][j]]*2&&bl[v]<bl[u])) x=fa[x][j];
}
ans[bl[v]]+=sz[x]-sz[v];
ans[bl[u]]+=sz[p]-sz[x];
}
for(int i=1;i<=cc;i++) vis[tt[i]]=0;
for(int i=head[u];i;i=nxt[i]) solve(to[i]);
}
int main() {
int n,m;
scanf("%d",&n);
lg[1]=0;for(int j=2;j<=n;j++)lg[j]=lg[j>>1]+1;
for(int i=1;i<n;i++) {
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v,0),add_edge(v,u,0);
}
init(1);
ecnt=0;for(int i=1;i<=n;i++)head[i]=0;
scanf("%d",&m);
while(m--) {
int ct=0,cc; scanf("%d",&cc);
for(int i=1;i<=cc;i++) {
int p; scanf("%d",&p);h[i]=p;
a[++ct]=(node){p,In[p]}; mark[p]=1;
}
sort(a+1,a+1+ct,cmp);
if(!mark[1])a[++ct]=(node){1,In[1]},mark[1]=2;
for(int i=1;i<ct;i++) {
int c=Lca(a[i].p,a[i+1].p);
if(!mark[c])a[++ct]=(node){c,In[c]},mark[c]=2;
}
sort(a+1,a+1+ct,cmp);
tp=0;
st[++tp]=a[1].p;
for(int i=2;i<=ct;i++) {
int u=a[i].p;
while(tp&&In[u]<In[st[tp]]||In[u]>Out[st[tp]]) tp--;
add_edge(st[tp],u,dep[u]-dep[st[tp]]);
st[++tp]=u;
}
dfs1(a[1].p);
dfs2(a[1].p);
solve(a[1].p);
for(int i=1;i<=cc;i++) printf("%d ",ans[h[i]]);
puts("");
for(int i=1;i<=ct;i++) mark[a[i].p]=head[a[i].p]=vis[a[i].p]=0; ecnt=0;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人