草稿
我们考虑现在已知点集,如何求最后的答案。发现答案就是求所有点构成的最小生成树的边权和减去最小生成树直径。
嗯……这个东西我好像不会动态维护啊。
哦,发现要回到最初转移到的村庄,那不就是两倍的最小生成树的边权吗,题目变简单了一点。
现在就是考虑动态维护最小生成树的边权和,我们考虑将边权下放。那么当前这棵树的答案就是最小生成树的点权和减去最上面的点的点权。
最小生成树的点权和可以用扫描线来搞。然后最上面的点就是所有点的公共 (lca) , (emm...),这个东西可以用线段树维护吗?
应该是可以的,每一次查询两个点的 (lca) 用 (st) 表,然后两两合并,单点修改是否存在,好像很可做。
然后这道题目就被我口胡出来了???
发现 (lca) 的部分没有问题,但是在求最小生成树点权的时候的扫描线好像是假的(前面做了一道很像的题,用扫描线可以搞,但这里发现不可以)。所以我们考虑如何给你一堆点,求这些点的最小生成树。
发现这是一道虚树的题而我以前从来都没有做过虚树啊……
所以说现在我们的目标就是考虑如何动态维护最小生成树的点权和……
我在处理连边的时候想到了鼠树,然后就知道了 (ETT) 。
跟巨佬 ( ext{Rui_R}) 交流过之后发现好像可以不用什么稀奇古怪的东西来做了。
题解
这里要讲的是一个两只 (log_2) 的低劣做法,仅供各位参考,希望能给您带来启发。
问题转化为动态求一个点集的最小生成树的边权和,发现这个东西不太好弄。
我们考虑到边权不是很好维护,所以就将边权下放点权,那么最终求答案的时候就需要两个东西:最小生成树的点权和 和 生成树最上方的点的点权(因为最上面的这个东西不在答案范围中)。
我们先来考虑后者,发现最上方的点实际上就是所有节点的 (lca) (注:此处是指所有点的最近公共祖先),也就是要动态维护一个点集的 (lca) 。
可以发现如果将 (lca(u,v)) 看成一种运算,发现他是完美满足结合律的,然后我们就可以用线段树来维护,这里不多赘述了。
然后考虑前者,发现这个东西除非用一些动态的高妙数据结构好像根本不能维护,但是我们可以发现,如果我们用类似于扫描线的做法,每一次加入或删除一个点的时候,将这个点到根的路径的 (tag) 全部 (+1) ,那么最后查询的整棵树中 (tag) 大于 (0) 的节点的权值和,实际上就是我们最后需要的最小生成树的点权和加上所有点的 (lca) 到根节点的路径的权值和,由于我们前面已经可以维护所有点的 (lca) 了,所有最后只需要减去后者(所有点的 (lca) 到根节点的路径的权值和)即可。
代码如下
#include<bits/stdc++.h>
using namespace std;
#define Lint long long
const int N=1e5+5;
int n,m;
struct Node{int dep,fa,top,son,size;Lint data,dis;}tr[N];
struct Edge{int nxt,to;Lint val;}e[N<<1];int fir[N];
void add(int u,int v,Lint w,int i){e[i]=(Edge){fir[u],v,w},fir[u]=i;}
void dfs1(int u)
{
tr[u].size=1,tr[u].top=u;
tr[u].dep=tr[tr[u].fa].dep+1;
tr[u].dis=tr[tr[u].fa].dis+tr[u].data;
for(int i=fir[u];i;i=e[i].nxt)
{
if(e[i].to==tr[u].fa) continue;
tr[e[i].to].fa=u;
tr[e[i].to].data=e[i].val;
dfs1(e[i].to);
tr[u].size+=tr[e[i].to].size;
if(tr[e[i].to].size>tr[tr[u].son].size)
tr[u].son=e[i].to;
}
}
int dfn[N],mp[N],cnt=0;
void dfs2(int u)
{
dfn[++cnt]=u,mp[u]=cnt;
if(tr[u].son)
{
tr[tr[u].son].top=tr[u].top;
dfs2(tr[u].son);
}
for(int i=fir[u];i;i=e[i].nxt)
{
if(e[i].to==tr[u].fa||e[i].to==tr[u].son) continue;
dfs2(e[i].to);
}
}
int lca(int u,int v)
{
while(tr[u].top!=tr[v].top)
{
if(tr[tr[u].top].dep<tr[tr[v].top].dep) swap(u,v);
u=tr[tr[u].top].fa;
}
if(tr[u].dep>tr[v].dep) swap(u,v);
return u;
}
bool alive[N];
Lint val[N];
struct Seg_Tree_data
{
struct Node{Lint data,sum;int tag;}tr[N<<3];
void up(int u)
{
if(tr[u].tag) tr[u].data=tr[u].sum;
else tr[u].data=tr[u<<1].data+tr[u<<1|1].data;
}
void build(int u,int l,int r,Lint a[])
{
tr[u].tag=tr[u].data=0;
if(l==r){tr[u].sum=a[l];return ;}
int mid=(l+r)>>1;
build(u<<1,l,mid,a);
build(u<<1|1,mid+1,r,a);
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void add(int u,int l,int r,int x,int y,int z)
{
if(x<=l&&r<=y){tr[u].tag+=z,up(u);return ;}
int mid=(l+r)>>1;
if(x<=mid) add(u<<1,l,mid,x,y,z);
if(y>mid) add(u<<1|1,mid+1,r,x,y,z);
up(u);
}
}t1;
struct Seg_Tree_lca
{
struct Node{int data;}tr[N<<2];
void up(int u)
{
if(!tr[u<<1].data){tr[u].data=tr[u<<1|1].data;return ;}
if(!tr[u<<1|1].data){tr[u].data=tr[u<<1].data;return ;}
tr[u].data=lca(tr[u<<1].data,tr[u<<1|1].data);
}
void chg(int u,int l,int r,int x)
{
if(l==r)
{
if(tr[u].data) tr[u].data=0;
else tr[u].data=l;
return ;
}
int mid=(l+r)>>1;
if(x<=mid) chg(u<<1,l,mid,x);
else chg(u<<1|1,mid+1,r,x);
up(u);
}
}t2;
void work(int u,int v,int w)
{
while(tr[u].top!=tr[v].top)
{
if(tr[tr[u].top].dep<tr[tr[v].top].dep) swap(u,v);
t1.add(1,1,n,mp[tr[u].top],mp[u],w);
u=tr[tr[u].top].fa;
}
if(tr[u].dep>tr[v].dep) swap(u,v);
t1.add(1,1,n,mp[u],mp[v],w);
}
int main()
{
cin>>n>>m;
for(int i=1,u,v,w;i<n;++i)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w,i<<1),add(v,u,w,i<<1|1);
}
dfs1(1),dfs2(1);
for(int i=1;i<=n;++i) val[i]=tr[dfn[i]].data;
t1.build(1,1,n,val);
while(m--)
{
int u;cin>>u;
if(!alive[u])
{
alive[u]=true;
work(1,u,1);
t2.chg(1,1,n,u);
}
else
{
alive[u]=false;
work(1,u,-1);
t2.chg(1,1,n,u);
}
// printf("%lld %d
",t1.tr[1].data,t2.tr[1].data);
printf("%lld
",2*(t1.tr[1].data-tr[t2.tr[1].data].dis));
}
return 0;
}