「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
#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[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<r) swap(l,r);int k=lg[r-l+1]-1;
		return Min(f[k][l],f[k][r-(1<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>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