虚树

我们每天都用心思索着,这究竟是为了什么呢?我想我也不知道,只是觉得如果人不思考问题就很无聊。

  • 我觉得虚树不是什么数据结构,就是一种技巧或者工具。它能把树中\(k\)个关键点以\(O(klogk)\)的复杂度变成一棵节点数\(2*k\)以内的树。并且构树的边的信息适用广泛(如最大值,和……)
    有两种构树方法:(我这里只讲第一种,第二种男鞋后面看心情)
    我们把所有节点按dfn排序,然后相邻两个求的lca加入点中再排序。
    用到stack,每个点在加入之前,一直弹栈,直到找到第一个是它祖先的点当做他虚数上的父亲。代码还是挺好写的,就是要求lca.(懒得写st表lca)
    然后题目之后就很灵活了,通常是问你一些数论问题。发正她还真是个工具人呢。

大工程

  • 题意:【HEOI2014】大工程
  • 思路:先构造虚数。然后求解树上最长链、最短链、所有链的和。我就是存Cnt,sum求和(应该大家都是这样),不过最长最短我受了直径的影响有点傻B,存储了最大次大(小),不过也无大碍
  • code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int 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];
ll inf=1e18,s1[N],Mn,Sum,Mx,dp[N],mn1[N],mn2[N],mx1[N],mx2[N],sum[N],cnt[N],len[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;
	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);
	}
	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 dfs(int u) {
	s1[u]=cnt[u]=mx1[u]=mx2[u]=0;
	mn1[u]=mn2[u]=inf;
	for(int i=head[u];i;i=nxt[i]) {
		int v=to[i];
		dfs(v);
		cnt[v]+=(mark[v]==1);
		ll tmp=s1[v]+cnt[v]*len[i];
//		printf("%lld %lld\n",cnt[u],tmp);
		Sum+=(cnt[u]+(mark[u]==1))*tmp+cnt[v]*s1[u];
		cnt[u]+=cnt[v],s1[u]+=tmp;
		tmp=mx1[v]+len[i];
		if(mx1[u]<tmp) mx2[u]=mx1[u],mx1[u]=tmp;
		else if(mx2[u]<tmp) mx2[u]=tmp;
		tmp=mn1[v]+len[i];
		if(mn1[u]>tmp) mn2[u]=mn1[u],mn1[u]=tmp;
		else if(mn2[u]>tmp) mn2[u]=tmp;
	}
	if(mark[u]==1) {Mn=min(Mn,mn1[u]);mn1[u]=0;}
	else Mn=min(Mn,mn1[u]+mn2[u]);
	Mx=max(Mx,mx1[u]+mx2[u]);
}
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);
			a[++ct]=(node){p,In[p]}; mark[p]=1;
		}
		sort(a+1,a+1+ct,cmp);
		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]]);
//			printf("!%d %d %d\n",st[tp],u,dep[u]-dep[st[tp]]);
			st[++tp]=u;
		}
		Mn=1e18,Mx=Sum=0;
		dfs(a[1].p);
		printf("%lld %lld %lld\n",Sum,Mn,Mx);
		for(int i=1;i<=ct;i++) mark[a[i].p]=head[a[i].p]=0; ecnt=0;
	}
	return 0;
}