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;
}

真的细节多,调了我好久。

posted @ 2019-02-19 20:34  XSamsara  阅读(197)  评论(0编辑  收藏  举报