「SDOI2015」寻宝游戏

「SDOI2015」寻宝游戏

链接:https://www.luogu.com.cn/problem/P3320


可以发现答案就是求当前存在点所构成最小生成树的边权和的两倍

那么考虑如何求存在点的最小生成树。

由于原图就是一棵树,那么最小生成树是唯一的,我们考虑把原图以 \(1\) 为根固定下来。那么所有存在点的 lca 一定是在所求得最小生成树上的。又因为存在的点是动态的,我们得找到一种办法维护动态点的 lca,由于 lca 满足结合律,即 \(lca(x,y,z)=lca(lca(x,y),z)=lca(x,lca(y,z))\)。那么我们可以用线段树分治来解决这个问题。

如果用 ST 表来求解 lca,那么动态维护 lca 的复杂度是 \(O(\log n)\)

然后考虑求最小生成树的边权和。我们可以将边权和转化为点权和。即每个点的点权等于它父亲连向它的那条边的边权。特殊地,对于根 \(1\),我们规定它的权值为 \(0\)

那么如何维护最小生成树边权和呢,我们发现最小生成树的边权和可以通过这种方式来求:

将所有存在点到根 \(1\) 的路径上的点染色,那么最小生成树的边权和就是所有被染色的点的点权和再减去 lca 到 \(1\) 路径上的点权和。

前者可以通过树剖加上一个类似于扫描线的线段树维护。

后者可以 dfs 预处理得到。

时间复杂度瓶颈在于树剖,总时间复杂度 \(O(n\log n\log n)\)

代码如下(树剖的线段树要开 \(8\) 倍不知道为什么):

#include<bits/stdc++.h>
#define ll long long
#define ls(k) (k<<1)
#define rs(k) (k<<1|1)
using namespace std;
const int MAXN = 1e5+5;
int n,m;
struct E
{
	int to;ll w;
};
vector <E> e[MAXN];
int dfn[MAXN],totdfn,dfn1[MAXN<<1],totdfn1,idx[MAXN],idx1[MAXN],match[MAXN];
int siz[MAXN],f[MAXN],son[MAXN],top[MAXN],dep[MAXN],opt[MAXN];
ll V[MAXN],Sum[MAXN],Dis[MAXN];
bool exist[MAXN];
struct lca_ST
{
	int f[21][MAXN<<1],lg[MAXN<<1];
	int Min(int x,int y)
	{
		if(dep[x]<=dep[y]) return x;
		else return y;
	}
	void init()
	{
		for(int i=1;i<=totdfn1;++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
		for(int i=1;i<=totdfn1;++i) f[0][i]=dfn1[i];
		for(int j=1;j<=lg[totdfn1];++j)
			for(int i=1;i+(1<<j)-1<=totdfn1;++i)
				f[j][i]=Min(f[j-1][i],f[j-1][i+(1<<(j-1))]);
	}
	int Q(int x,int y)
	{
		if(x==0||y==0) return x|y;
		int l=idx1[x],r=idx1[y];
		if(l>r) swap(l,r);int k=lg[r-l+1]-1;
		return Min(f[k][l],f[k][r-(1<<k)+1]);
	}
}ST;
void dfs1(int p,int fa)
{
	dfn1[++totdfn1]=p;idx1[p]=totdfn1;f[p]=fa;dep[p]=dep[fa]+1;siz[p]=1;
	for(int i=0;i<e[p].size();++i)
	{
		int to=e[p][i].to;ll w=e[p][i].w;
		if(to==fa) continue;
		Dis[to]=Dis[p]+w;dfs1(to,p);V[to]=w;
		siz[p]+=siz[to];if(siz[to]>siz[son[p]]) son[p]=to;
		dfn1[++totdfn1]=p;
	}
}
void dfs2(int p,int fa,int t)
{
	top[p]=t;dfn[++totdfn]=p;idx[p]=totdfn;
	if(son[p]) dfs2(son[p],p,t);
	for(int i=0;i<e[p].size();++i)
	{
		int to=e[p][i].to;
		if(to==son[p]||to==fa) continue;
		dfs2(to,p,to);
	}
}
struct Tree_V
{
	ll V[MAXN<<3];int cnt[MAXN<<3];
	Tree_V(){memset(V,0,sizeof V);memset(cnt,0,sizeof cnt);}
	void pushup(int k,int l,int r)
	{
		if(cnt[k]) V[k]=Sum[r]-Sum[l-1];
		else V[k]=V[ls(k)]+V[rs(k)];
	}
	void upd(int le,int ri,int k,int l,int r,int c)
	{
		if(le<=l&&r<=ri){cnt[k]+=c;pushup(k,l,r);return ;}
		int mid=l+r>>1;
		if(le<=mid) upd(le,ri,ls(k),l,mid,c);
		if(ri>mid) upd(le,ri,rs(k),mid+1,r,c);
		pushup(k,l,r);
	}
	void U(int x,int c)
	{
		while(x)
		{
			upd(idx[top[x]],idx[x],1,1,n,c);
			x=f[top[x]];
		}
	}
}T1;
struct Tree_LCA
{
	int node[MAXN<<2];
	Tree_LCA(){memset(node,0,sizeof node);}
	void Add(int le,int ri,int k,int l,int r,int x)
	{
		if(le<=l&&r<=ri) {node[k]=ST.Q(node[k],x);return ;}
		int mid=l+r>>1;
		if(le<=mid) Add(le,ri,ls(k),l,mid,x);
		if(ri>mid) Add(le,ri,rs(k),mid+1,r,x);
	}
	int Q(int pos,int k,int l,int r)
	{
		if(l==r) return node[k];
		int mid=l+r>>1;
		if(pos<=mid) return ST.Q(node[k],Q(pos,ls(k),l,mid));
		else return ST.Q(node[k],Q(pos,rs(k),mid+1,r));
	}
}T2;
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<n;++i)
	{
		int u,v;ll w;
		scanf("%d %d %lld",&u,&v,&w);
		e[u].push_back(E{v,w});
		e[v].push_back(E{u,w});
	}
	dfs1(1,0);dfs2(1,0,1);ST.init();
	for(int i=1;i<=n;++i) Sum[i]=Sum[i-1]+V[dfn[i]];
	for(int i=1;i<=m;++i)
	{
		int t;scanf("%d",&t);opt[i]=t;
		if(match[t]==0) match[t]=i;
		else T2.Add(match[t],i-1,1,1,m,t),match[t]=0;
	}
	for(int i=1;i<=n;++i) if(match[i]) T2.Add(match[i],m,1,1,m,i);
	for(int i=1;i<=m;++i)
	{
		exist[opt[i]]^=1;
		if(exist[opt[i]]==0) T1.U(opt[i],-1);
		else T1.U(opt[i],1);
		int lca=T2.Q(i,1,1,m);
		if(lca==0) printf("0\n");
		else printf("%lld\n",2*(T1.V[1]-Dis[lca]));
	}
}
posted @ 2022-01-05 16:39  夜空之星  阅读(24)  评论(0编辑  收藏  举报