大工程

链接

借此题记录学习半个月后我对虚树更为深入的理解。我感觉我这个人对新知识就是这样,一开始能听懂思想,但是就是不知道应该怎么写。然后平时闲下来就花时间思考思考,突然就会有一瞬间,就像开了窍一样明白了那些神神鬼鬼天书一样的代码究竟想干什么。我觉得这算是一种蜕变,所以一般知识点我会写两篇博客。就比如cdq分治我写了“cdq分治初步”和“[CH弱省胡策R2]TATT”两篇博客,就是这个道理。说句题外话,我还记得想通cdq的那个中午,我去人员极度密集的食堂抢饭,人群由于要去不同的窗口而自发地分成一些若有若无的块状队伍。我若有所思而兴趣盎然地盯着那些大同小异的校服,回头一看,突然看见了yxy从门口进来。然后就突然想通了。很传奇的经历对不对。

闲言少叙书归正传。虚树最重要的部分就是如何建树。我认为建虚树的那个过程本质是把新点向已有的虚树上挂。对dfn排序的作用是保证了那些特殊点是从左往右(严谨地说是按照一个特定的顺序)从上到下访问的,也就是说新的点只可能挂在已有树的右下角(好抽象啊啊啊)。第一个while就是要把可以利用的那个正确的右下角给它露出来,而接下来要做的就是把它挂上去。栈的作用则是维护了这棵已有树的最右端,而栈顶恰好就是树的右下角。反映在代码上就是不断弹栈然后加边即可。

建树模板:

void build(){
	push(a[1]);
	for(int i=2;i<=m;i++){
		int lc=lca(a[i],st[top]);
		while(top>1&&d[lc]<d[st[top-1]]){add(st[top-1],st[top]);top--;}
		if(st[top]!=lc)add(lc,st[top--]);
		push(lc);push(a[i]);
	}
	while(top>1){add(st[top-1],st[top]);top--;}
	top=0;return;
}

然后有很多需要注意的地方。

  • “时间快清”要清除的是每个点的head,而需要注意的是每条边两个端点的head和t都需要更新(调了好久啊)。
  • 这种方法建出来树的树根需要特殊处理,即在加边的时候额外yy并更新一下,找到所有加边端点中深度最小的点即可。当然你也可以默认加节点1进去,但缺点是这样就无法保证这棵虚树的所有末端(度为1的节点)均为特殊点,在一些问题中可能会造成很大的麻烦。加边代码如下。
inline void add(int fr,int to){
	if(nt!=t[fr])t[fr]=nt,head[fr]=0,minn=N;
	if(nt!=t[to])t[to]=nt,head[to]=0;
	if(minn>d[fr])minn=d[fr],root=fr;
	esum++;e[esum].t=to;e[esum].next=head[fr];head[fr]=esum;return;
}

然后就没什么了。另外有个小点就是,这道题告诉我们统计每条路径贡献时可以把问题转换成每条边会被更新几次。

代码AC记录

#include<cstdio>
#include<cstring>
#include<algorithm>
//#define zczc
#define ll long long
#define alle for(int i=head[wh],th;i;i=e[i].next)
#define che th=e[i].t;if(th==fa)continue
using namespace std;
const int N=1000010,maxn=1e9,S=30;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}wh*=f;return;
}
inline int max(int s1,int s2){return s1<s2?s2:s1;}
inline int min(int s1,int s2){return s1<s2?s1:s2;}
int m,n,esum,head[N],nt,t[N],root,minn,nowdfn,lg[N],d[N],dfn[N],nxt[N][S];
struct edge{int t,next;}e[N<<1];
inline void init(){esum=0;nt++;return;}
inline void add(int fr,int to){
	if(nt!=t[fr])t[fr]=nt,head[fr]=0,minn=N;
	if(nt!=t[to])t[to]=nt,head[to]=0;
	if(minn>d[fr])minn=d[fr],root=fr;
	esum++;e[esum].t=to;e[esum].next=head[fr];head[fr]=esum;
	return;
}
void pre1(int wh,int fa,int deep){
	dfn[wh]=++nowdfn;d[wh]=deep;nxt[wh][0]=fa;
	for(int i=1;i<=lg[d[wh]];i++)nxt[wh][i]=nxt[nxt[wh][i-1]][i-1];
	alle{
		che;pre1(th,wh,deep+1);
	}
	return;
}
inline void swap(int &s1,int &s2){int s3=s1;s1=s2;s2=s3;return;}
int lca(int s1,int s2){
	if(d[s1]<d[s2])swap(s1,s2);
	for(int i=lg[d[s1]];i>=0;i--)if(d[nxt[s1][i]]>=d[s2])s1=nxt[s1][i];
	if(s1==s2)return s1;
	for(int i=lg[d[s1]];i>=0;i--)if(nxt[s1][i]!=nxt[s2][i])s1=nxt[s1][i],s2=nxt[s2][i];
	return nxt[s1][0];
}
int a[N],st[N],top,s1,s2,num[N],f1[N],f2[N];bool in[N];ll ans;
inline bool cmp(int s1,int s2){return dfn[s1]<dfn[s2];}
inline void push(int wh){if(top!=0&&wh==st[top])return;st[++top]=wh;return;}
void build(){
	push(a[1]);
	for(int i=2;i<=m;i++){
		int lc=lca(a[i],st[top]);
		while(top>1&&d[lc]<d[st[top-1]]){add(st[top-1],st[top]);top--;}
		if(st[top]!=lc)add(lc,st[top--]);push(lc);push(a[i]);
	}
	while(top>1){add(st[top-1],st[top]);top--;}
	top=0;return;
}
void solve1(int wh,int fa){
	num[wh]=in[wh];
	alle{
		che;solve1(th,wh);num[wh]+=num[th];
		ans+=(ll)(d[th]-d[wh])*num[th]*(m-num[th]);
	}
	return;
}
void solve2(int wh,int fa){
	f1[wh]=f2[wh]=0;
	alle{
		che;solve2(th,wh);
		int nd=(d[th]-d[wh])+f1[th];
		if(f1[wh]<nd)f2[wh]=f1[wh],f1[wh]=nd;else f2[wh]=max(f2[wh],nd);
		if(ans<f1[wh]+f2[wh])ans=f1[wh]+f2[wh];
	}
	return;
}
void solve3(int wh,int fa){
	f1[wh]=f2[wh]=maxn;
	alle{
		che;solve3(th,wh);
		int nd=(d[th]-d[wh])+f1[th];
		if(f1[wh]>nd)f2[wh]=f1[wh],f1[wh]=nd;else f2[wh]=min(f2[wh],nd);
		if(in[wh]&&ans>f1[wh])ans=f1[wh];if(f1[wh]+f2[wh]<ans)ans=f1[wh]+f2[wh];
	}
	if(in[wh])f1[wh]=0;return;
}
signed main(){
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	lg[0]=1;for(int i=1;i<N;i++)lg[i]=lg[i>>1]+1;read(m);
	for(int i=1;i<m;i++){read(s1);read(s2);add(s1,s2);add(s2,s1);}
	pre1(1,0,1);read(n);
	while(n--){
		init();read(m);
		for(int i=1;i<=m;i++){read(a[i]);in[a[i]]=true;}
		sort(a+1,a+m+1,cmp);build();
		ans=0;solve1(root,0);printf("%lld ",ans);
		ans=maxn;solve3(root,0);printf("%lld ",ans);
		ans=0;solve2(root,0);printf("%lld\n",ans);
		for(int i=1;i<=m;i++)in[a[i]]=false;
	}
	return 0;
}
posted @ 2022-01-18 22:06  Feyn618  阅读(79)  评论(0编辑  收藏  举报