異象石
阿新 • • 發佈:2022-01-16
給定一個支援增加和刪除的點集,求將點集聯通的邊集的最小值
引理: 將點按照dfn環排列後 求相鄰兩點的距離和就是答案的二倍
嘗試證明:
#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; }