Loading

关于动态维护树中点集直径的研究

注:下文讲到的所有方法均依赖于树的边权为正数。

例题:P2056

是括号序动态维护的方法,这里不讲。

注意到一个结论:设 \(S,T(S\cap T=\varnothing)\) 为两个树中的点集

\(f(S)\) 为一个大小为 \(2\) 集合,其中两个点分别为 \(S\) 集合中直径的两个端点(多个取任意)。

则有结论:\(\exists\ x,y\in f(S)\cup f(T),x\neq y\),满足 \(x,y\)\(S\cup T\) 集合中直径的两个端点。请读者不难自证。

那么,根据这个结论,我们考虑利用线段树来维护区间直径。合并区间时直接暴力合并计算即可。

现在如果要单 \(\log\) 维护的话需要 \(O(1)\) 求两点的 \(\text{dis}\),即需要 \(O(1)\)\(\text{lca}\)

\(\text{lca}\) 的六种求法,不会的这里学习,我推荐 \(\texttt{dfs}\) 序来求。

$\texttt{code}$
#include<bits/stdc++.h>
#define P pair<int,int>
#define fi first
#define se second
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=1e5+5;
int n,m,tot,head[N],d[N],id[N],_id[N],mn[N][25],cnt;
bool a[N];P b[N<<2];
struct edge{int to,nex;}e[N<<1];
inline void add(int u,int v)
{
	e[++tot]={v,head[u]};head[u]=tot;
	e[++tot]={u,head[v]};head[v]=tot;
}
inline int MN(int x,int y){return d[x]<d[y]?x:y;}
void dfs(int x,int fa)
{
	d[x]=d[mn[id[x]=++tot][0]=fa]+1;_id[tot]=x;
	for(int i=head[x];i;i=e[i].nex){int to=e[i].to;if(to!=fa) dfs(to,x);}
}
inline int lca(int u,int v)
{
	if(u==v) return u;u=id[u],v=id[v];u>v&&(swap(u,v),1);
	int t=__lg(v-(u++));return MN(mn[u][t],mn[v-(1<<t)+1][t]);
}
inline int dis(int u,int v){return (u==-1||v==-1)?-1:d[u]+d[v]-(d[lca(u,v)]<<1);}
inline P hb(P x,P y)
{
	int a=dis(x.fi,y.fi),b=dis(x.fi,y.se),c=dis(x.se,y.fi),d=dis(x.se,y.se),e=dis(x.fi,x.se),f=dis(y.fi,y.se),g=max({a,b,c,d,e,f});
	if(e==g) return x;if(f==g) return y;
	return {(a==g||b==g)?x.fi:x.se,(a==g||c==g)?y.fi:y.se};
}
void build(int l,int r,int wz)
{
	if(l==r) return b[wz]={l,l},void();int mid=(l+r)>>1;
	build(l,mid,wz<<1);build(mid+1,r,wz<<1|1);b[wz]=hb(b[wz<<1],b[wz<<1|1]);
}
void updata(int l,int r,int wz,int x)
{
	if(l==r) return b[wz]=a[x]?(P){x,x}:(P){-1,-1},void();
	int mid=(l+r)>>1;
	if(x<=mid) updata(l,mid,wz<<1,x);else updata(mid+1,r,wz<<1|1,x);
	b[wz]=hb(b[wz<<1],b[wz<<1|1]);
}
int main()
{
	scanf("%d",&n);int u,v,x;char c[1];
	for(int i=1;i<n;i++) scanf("%d%d",&u,&v),add(u,v);scanf("%d",&m);
	for(int i=1;i<=n;i++) a[i]=1;cnt=n;tot=0;dfs(1,0);
	for(int i=1;i<=__lg(n);i++) for(int j=1;j+(1<<(i-1))<=n;j++) mn[j][i]=MN(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);
	build(1,n,1);
	while(m--)
	{
		scanf("%s",c);
		if(c[0]=='C') scanf("%d",&x),a[x]^=1,a[x]?cnt++:cnt--,updata(1,n,1,x);
		else
		{
			if(!cnt){puts("-1");continue;}
			if(cnt==1){puts("0");continue;}
			u=b[1].fi,v=b[1].se;x=dis(u,v);printf("%d\n",x);
		}
	}
	return 0;
}

练习 \(1\)CF1413,基本就是板子加一点小技巧。大家最好不要用我这种写法维护这个事情(建议用括号序,参考题解),否则会超时。

强行卡过的 $\texttt{code}$
#include<bits/stdc++.h>
#define P pair<int,int>
#define fi first
#define se second
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
namespace IO
{
	const int _Pu=2e7+5,_d=32;
	char buf[_Pu],obuf[_Pu],*p1=buf+_Pu,*p2=buf+_Pu,*p3=obuf,*p4=obuf+_Pu-_d;
	inline void fin()
	{
		memmove(buf,p1,p2-p1);
		int rlen=fread(buf+(p2-p1),1,p1-buf,stdin);
		if(p1-rlen>buf) buf[p2-p1+rlen]=EOF;p1=buf;
	}
	inline void fout(){fwrite(obuf,p3-obuf,1,stdout),p3=obuf;}
	inline int rd()
	{
		if(p1+_d>p2) fin();int isne=0,x=0;
		for(;!isdigit(*p1);++p1) isne=(*p1=='-');x=(*p1++-'0');
	    for(;isdigit(*p1);++p1) x=x*10+(*p1-'0');
		if(isne) x=-x;return x;
	}
	inline void wr(int x,char end='\n')
	{
		if(!x) return *p3++='0',*p3++=end,void();
		if(x<0) *p3++='-',x=-x;
		char sta[20],*top=sta;
		do{*top++=(x%10)+'0';x/=10;}while(x);
		do{*p3++=*--top;}while(top!=sta);(*p3++)=end;
	}
}
const int N=5e5+5;
int n,m,tot,head[N],d[N],id[N],_id[N],siz[N],mn[25][N],o,rt0,rt1,ls[N<<3],rs[N<<3],L,R;
bool a[N];P b[N<<3],E[N];
struct edge{int to,nex,w;}e[N<<1];
inline void add(int u,int v,int w)
{
	e[++tot]={v,head[u],w};head[u]=tot;
	e[++tot]={u,head[v],w};head[v]=tot;
}
#define MN(x,y) d[x]<d[y]?x:y
inline void dfs(int x,int fa)
{
	d[x]=d[mn[0][id[x]=++tot]=fa]+1;_id[tot]=x;siz[x]=1;
	for(int i=head[x];i;i=e[i].nex){int to=e[i].to;if(to^fa) a[to]=a[x]^e[i].w,dfs(to,x),siz[x]+=siz[to];}
}
inline int lca(int u,int v)
{
	if(u==v) return u;u=id[u],v=id[v];u>v&&(swap(u,v),1);
	int t=__lg(v-(u++));return MN(mn[t][u],mn[t][v-(1<<t)+1]);
}
#define dis(u,v) ::d[u]+::d[v]-(::d[lca(u,v)]<<1)
inline void pushup(int wz)
{
	P x=b[ls[wz]],y=b[rs[wz]];
	if(x.fi==-1) return b[wz]=y,void();if(y.fi==-1) return b[wz]=x,void();
	int a=dis(x.fi,y.fi),b=dis(x.fi,y.se),c=dis(x.se,y.fi),d=dis(x.se,y.se),e=dis(x.fi,x.se),f=dis(y.fi,y.se),g=max({a,b,c,d,e,f});
	if(e==g) return ::b[wz]=x,void();if(f==g) return ::b[wz]=y,void();
	::b[wz]={(a==g||b==g)?x.fi:x.se,(a==g||c==g)?y.fi:y.se};
}
inline void build(int l,int r,int &wz)
{
	wz=++tot;int mid=(l+r)>>1;
	if(l==r) return b[wz]=(a[_id[l]]^o)?(P){-1,-1}:(P){_id[l],_id[l]},void();
	build(l,mid,ls[wz]);build(mid+1,r,rs[wz]);pushup(wz);
}
inline void updata(int l,int r,int &wz,int &wz1)
{
	if(L<=l&&r<=R) return swap(wz,wz1);
	int mid=(l+r)>>1;
	if(L<=mid) updata(l,mid,ls[wz],ls[wz1]);
	if(mid<R) updata(mid+1,r,rs[wz],rs[wz1]);
	pushup(wz);pushup(wz1);
}
int main()
{
	n=IO::rd();int u,v,x;
	for(int i=1;i<n;i++) u=IO::rd(),v=IO::rd(),x=IO::rd(),add(u,v,x),E[i]={u,v};m=IO::rd();tot=0;dfs(1,0);
	for(int i=1;i<=__lg(n);i++) for(int j=1;j+(1<<(i-1))<=n;j++) mn[i][j]=MN(mn[i-1][j],mn[i-1][j+(1<<(i-1))]);
	tot=0;build(1,n,rt0);o=1;build(1,n,rt1);
	while(m--)
	{
		x=IO::rd();int t=d[E[x].fi]>d[E[x].se]?E[x].fi:E[x].se;L=id[t],R=id[t]+siz[t]-1;
		updata(1,n,rt0,rt1);IO::wr(max(dis(b[rt0].fi,b[rt0].se),dis(b[rt1].fi,b[rt1].se)));
	}
	return IO::fout(),0;
}

练习 \(2\)P10238,参考我的口胡

posted @ 2023-06-01 13:05  HaHeHyt  阅读(231)  评论(0编辑  收藏  举报