LibreOJ 6514. 「雅礼集训 2018 Day10」文明【虚树+LCA】
6514. 「雅礼集训 2018 Day10」文明
【题目描述】
【题解】
考虑笨蛋的写法,可以用LCA求出1号和其他点的中点,然后DFS搜索Size大小即可,但是,复杂度显然要炸,但是我们会发现,所需要的点不多,所以我们可以用虚数优化。
代码如下
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=500005,LOG2=19;
int n,Q,K,Tim,Ans,Siz[MAXN],a[MAXN],Dep[MAXN],Fa[MAXN][20],vis[MAXN],hv[MAXN];
int cnt,IN[MAXN],OUT[MAXN],que[2*MAXN],Top,Stk[MAXN],tot;
struct Edge{
int tot,lnk[MAXN],nxt[MAXN<<1],son[MAXN<<1];
void Add(int x,int y){nxt[++tot]=lnk[x];lnk[x]=tot;son[tot]=y;}
}E,S;
#include<cctype>
int read(){
int ret=0;char ch=getchar();bool f=1;
for(;!isdigit(ch);ch=getchar()) f^=!(ch^'-');
for(; isdigit(ch);ch=getchar()) ret=ret*10+ch-48;
return f?ret:-ret;
}
void DFS(int x,int fa){
Dep[x]=Dep[fa]+1;Fa[x][0]=fa;Siz[x]=1;IN[x]=++cnt;
for(int j=E.lnk[x];j;j=E.nxt[j]) if(E.son[j]!=fa) DFS(E.son[j],x),Siz[x]+=Siz[E.son[j]];
OUT[x]=++cnt;
}
void INIT(){
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++) Fa[i][j]=Fa[Fa[i][j-1]][j-1];
}
void Count_Ans(int x){
Ans++,vis[x]=Tim;
for(int j=E.lnk[x];j;j=E.nxt[j]) if(vis[E.son[j]]!=Tim) Count_Ans(E.son[j]);
}
int LCA(int p,int q){
if(Dep[p]<Dep[q]) swap(q,p);
int Del=Dep[p]-Dep[q];
for(int j=0;(1<<j)<=Del;j++) if(Del&(1<<j)) p=Fa[p][j];
if(p==q) return p;
for(int j=LOG2;j>=0;j--)
if(Fa[p][j]^Fa[q][j]) p=Fa[p][j],q=Fa[q][j];
p=Fa[p][0],q=Fa[q][0];
return p;
}
int Jump(int p,int Del){for(int j=0;(1<<j)<=Del;j++) if(Del&(1<<j)) p=Fa[p][j];return p;}
bool cmp(int x,int y){return (x<0?OUT[-x]:IN[x])<(y<0?OUT[-y]:IN[y]);}
void Count(int x,int fa){
if(hv[x]==Tim){
if(LCA(x,a[1])!=x) Ans+=Siz[x];else Ans+=n-Siz[Jump(a[1],Dep[a[1]]-Dep[x]-1)];
return;
}
for(int j=S.lnk[x];j;j=S.nxt[j])
if(S.son[j]!=fa) Count(S.son[j],x);
}
int Work(){
Tim++;Ans=Top=0;
for(int i=2;i<=K;i++){
int fa=LCA(a[1],a[i]),Len=Dep[a[1]]+Dep[a[i]]-2*Dep[fa],x;
if(Dep[a[i]]-Dep[fa]>=(Len-1)/2) x=Jump(a[i],(Len-1)/2);else x=Jump(a[1],Len/2+1);
if(vis[x]!=Tim) vis[x]=Tim,que[++Top]=x,hv[x]=Tim;
}
if(vis[a[1]]!=Tim) vis[a[1]]=Tim,que[++Top]=a[1];
if(vis[1]!=Tim) vis[1]=Tim,que[++Top]=1;
sort(que+1,que+1+Top,cmp);
for(int i=2;i<=Top;i++){
int fa=LCA(que[i],que[i-1]);
if(vis[fa]!=Tim) vis[que[++Top]=fa]=Tim;
}
for(int i=1;i<=Top;i++) S.lnk[que[i]]=0;S.tot=0;S.lnk[0]=0;
for(int i=1,END=Top;i<=END;i++) que[++Top]=-que[i];
sort(que+1,que+1+Top,cmp);Stk[tot=0]=0;
for(int i=1;i<=Top;i++)
if(que[i]>0){if(tot) S.Add(Stk[tot],que[i]),S.Add(que[i],Stk[tot]);Stk[++tot]=que[i];}else tot--;
Count(a[1],0);
return n-Ans;
}
int main(){
n=read(),Q=read();
for(int i=1;i<n;i++){int x=read(),y=read();E.Add(x,y),E.Add(y,x);}
DFS(1,0);INIT();
for(int j=Q;j;j--){
K=read();
for(int i=1;i<=K;i++) a[i]=read();
printf("%d\n",Work());
}
return 0;
}
真的细节多,调了我好久。