异象石
给定一个支持增加和删除的点集,求将点集联通的边集的最小值
引理: 将点按照dfn环排列后 求相邻两点的距离和就是答案的二倍
尝试证明:
我们发现dfn相对大的节点 他们的了lca的的dfn就相对较小
如果一个节点的祖先的链上,经过dfn小的一定会经过dfn大的
对于三个点\(a,b,c\),我们假设\(dfn[a]<dfn[b] dfn[c]\)
有\(dfn[lca(b,c)] \le dfn[lca(a,b)]\) 设置为\(m_1,m_2\)
从a的角度来看:\(m_1,m_2\)都是a的祖先
\(a \rightarrow b : a\rightarrow m_1 \rightarrow b\)
\(a \rightarrow c : a\rightarrow m_1 \rightarrow m_2 \rightarrow b\)
因此一定会覆盖两遍
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
const int N=1e5+10;
typedef long long ll;
int read()
{
int x=0,f=0,c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return f?-x:x;
}
struct Edge
{
int to,next,w;
}e[N*2];
int head[N],cnt;
void _add(int a,int b,int c){ e[++cnt]=(Edge){b,head[a],c}; head[a]=cnt;}
void add(int a,int b,int c){ _add(a,b,c); _add(b,a,c);}
int sz[N],top[N],fa[N],son[N],dfn[N];
ll dep[N];
int tim;
void dfs1(int x)
{
sz[x]=1; dfn[x]=++tim;
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if(y==fa[x]) continue;
dep[y]=dep[x]+e[i].w; fa[y]=x;
dfs1(y);
sz[x]+=sz[y];
if(sz[son[x]]<sz[y]) son[x]=y;
}
}
void dfs2(int x,int tp)
{
top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
struct Node{ int id,tim;};
bool operator<( const Node &a,const Node &b){ return a.tim<b.tim;}
set< Node > s;
typedef set<Node>::iterator It;
int LCA(int x,int y)
{
while(top[x]!=top[y])
dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
ll getdis(int x,int y) { return dep[x]+dep[y]-2*dep[LCA(x,y)];}
ll upd(int x)
{
if(s.size()==1) return 0;
It it=s.find( (Node){x,dfn[x]} ); It tmp=it; It pre,nxt;
if(tmp==s.begin()) pre=--s.end(),nxt=++tmp;
else if(tmp==--s.end()) pre=--tmp,nxt=s.begin();
else pre=--tmp,tmp++,nxt=++tmp;
return getdis(pre->id,it->id)+getdis(it->id,nxt->id)-getdis(pre->id,nxt->id);
}
int n,m;
ll ans;
int main()
{
n=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read(),z=read();
add(x,y,z);
}
m=read();
dfs1(1); dfs2(1,1);
while(m--)
{
char c=getchar();
if(c=='?') printf("%lld\n",ans>>1),getchar();
else
{
int x=read();
if(c=='+')
{
s.insert( (Node){x,dfn[x]} );
ans=ans+upd(x);
}
else ans=ans-upd(x),s.erase( (Node){x,dfn[x]} );
}
}
return 0;
}
点集问题按照dfn排序后再考虑 可能会有特殊性质

浙公网安备 33010602011771号