仮想ツリー
問題ツリーのクラスの治療
このような質問は、ポイント数は、ツリーのポイント数よりもはるかに小さい尋ねる、いくつかの側面と一緒にカウントすることができます
その後、我々はそれに唯一のポイントを保持する必要があります
明らかに、キーポイントと変曲点(一部はLCAあり)
二十から二LCAおよび重量を得るために全ての点の仮想ツリーで得ることができる、DFSソート順に従って各点を考えます
の構築
私たちは、スタックと内部単調なシーケンスDFSによって増分スタック要素をコンクリートの建物を達成します
すべてのキーのDFSオーダーへのアクセスをクリックして、各点のために追加される\(のP- \)、2つのケースがあります。
- \(P \)及び頂部要素は、\(X \) LCAである(X \)を\、説明\(P \)における(X \)\内部スタックサブツリー
- それ以外の場合は、特定のアクセスのサブツリーの最上位要素は、(順番DFS)完了し、建物を考えます
頂部要素が設けられている\(X \) 、第二の要素である\(Y \)
- もし\(DFN [Y]> DFN [LCA] \) 、接続されていてもよい側\(Y \) - > \(X \) 、\(X \)スタックのうち。
- もし\(DFN [Y] = DFN [LCA] \) 、すなわち\(LCA Y = \) 、偶数側$ lca->のx $、この時間\(LCA(Y)\)サブツリーは、建物を終えました。
- もし\(DFN [Y] <DFN [LCA] \) 、すなわち\(LCA \)で\(Y、X \)も側との間の\(LCA-> X \)、\ (X \)のうちスタック、その後、\(LCA \)スタック。このサブツリーで、LCA(BREAK)の構築が完了
コード:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
const int N = 1000010,M=2000010,K = 23,inf=192608170;
using namespace std;
int n;
inline int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
struct node{
int v,nex;
};
ll ans1,ans2,f[N],maxs[N],mins[M],sz[N];
int fa[N][23],dfn[N],tim,bo[N],pi[N],st[N],dep[N],dis[N],num,bq[N];
struct graph{
node edge[M];int top,head[N];
void add(int u,int v){
edge[++top].v=v;edge[top].nex=head[u];head[u]=top;
}
void dfs1(int x){
dfn[x]=++tim;
for(int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i;i=edge[i].nex){
int v=edge[i].v;
if(v!=fa[x][0])
dis[v]=dis[x]+1,dep[v]=dep[x]+1,fa[v][0]=x,dfs1(v);
}
}
void dfs2(int x){
sz[x]=bo[x],maxs[x]=0,mins[x]=inf,f[x]=0;
for(int i=head[x];i;i=edge[i].nex){
int v=edge[i].v,d=dis[v]-dis[x];
dfs2(v);sz[x]+=sz[v];
ans1=min(ans1,mins[x]+mins[v]+d),ans2=max(ans2,maxs[x]+maxs[v]+d);
mins[x]=min(mins[x],mins[v]+d),maxs[x]=max(maxs[x],maxs[v]+d);
f[x]+=f[v]+1ll*d*(num-sz[v])*sz[v];
}
if(bo[x]) ans1=min(ans1,mins[x]),ans2=max(ans2,maxs[x]),mins[x]=0;
head[x]=0;
}
}g1,g2;
int q;
int cmp(int a,int b){
return dfn[a]<dfn[b];
}
int lca(int a,int b){
if(dep[a]<dep[b]) swap(a,b);
for(int h=dep[a]-dep[b],i=20;i>=0;i--){
if(h>=(1<<i)){
h-=(1<<i);
a=fa[a][i];
}
}
if(a==b) return a;
for(int i=20;i>=0;i--) if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
void work(){
sort(pi+1,pi+num+1,cmp);
int tp=0;
for(int i=1;i<=num;i++){
if(!tp){
st[++tp]=pi[i];continue;
}
int u=lca(st[tp],pi[i]);
while(dfn[u]<dfn[st[tp]]){
if(dfn[u]>=dfn[st[tp-1]]){
g2.add(u,st[tp]);
if(st[--tp]!=u) st[++tp]=u;
break;
}
g2.add(st[tp-1],st[tp]);tp--;
}
st[++tp]=pi[i];
}
while(tp>1) g2.add(st[tp-1],st[tp]),tp--;
ans1=inf,ans2=0,g2.dfs2(st[1]);
printf("%lld %d %d\n",f[st[1]],ans1,ans2);
for(int i=1;i<=num;i++) bo[pi[i]]=0;
for(int i=1;i<=g2.top;i++) g2.edge[i].nex=g2.edge[i].v=0;g2.top=0;
}
int main(){
n=read();g1.top=g2.top=0;
for(int i=1,u,v;i<n;i++){
u=read(),v=read();
g1.add(u,v);g1.add(v,u);
}
g1.dfs1(1);q=read();
for(int i=1;i<=q;i++){
num=read();
for(int j=1;j<=num;j++){
pi[j]=read();bo[pi[j]]=1;
}
work();
}
return 0;
}