【SPOJ2666】QTree4【链分治】

传送门

题意:给一棵带边权的树,每个点开始时为白色,维护两种操作:

1.改变一个点的颜色(白变黑,黑变白)
2.询问最远的两个白点之间的距离

树分治国集论文

链分治的本质其实就是树链剖分。它们的区别是树剖维护路径询问,链分治维护全局路径。

动态点分治需要重新建一棵树,而链分治分出来的是链,本身就可以用数据结构维护。所以链分治本身就可以资瓷修改。

对于每一条链单独用数据结构维护与当前链有交集的路径信息。

对于本题,先树链剖分

D i , D i ′ D_i,D_i' Di,Di为节点 i i i沿虚边往下走到某个白点的最长和次长长度。如果不存在,记为 − I N F -INF INF

用线段树维护一条链。对于一条链上的一个区间 [ L , R ] [L,R] [L,R]

l m a x = m a x { d i s t ( L , i ) + D i } lmax=max\{dist(L,i)+D_i\} lmax=max{dist(L,i)+Di}

r m a x = m a x { D i + d i s t ( i , R ) } rmax=max\{D_i+dist(i,R)\} rmax=max{Di+dist(i,R)}

即: l m a x lmax lmax表示 L L L沿这条链往下走(可以不走),在 [ L , R ] [L,R] [L,R]中的某个点拐出去,往下走(可以不走)到某个白点经过的最长长度。注意没有要求起点是白色 r m a x rmax rmax同理。

再记录 a n s ans ans表示有交集的最长长度

然后用类似最大子段和的方式合并。

对于一个线段树节点 p p p,左右儿子 l c , r c lc,rc lc,rc

l m a x [ p ] = m a x { l m a x [ l c ] , d i s t ( L , m i d + 1 ) + l m a x [ r c ] } lmax[p]=max\{lmax[lc],dist(L,mid+1)+lmax[rc]\} lmax[p]=max{lmax[lc],dist(L,mid+1)+lmax[rc]}

r m a x [ p ] = m a x { r m a x [ l c ] + d i s t ( m i d , R ) , r m a x [ r c ] } rmax[p]=max\{rmax[lc]+dist(mid,R),rmax[rc]\} rmax[p]=max{rmax[lc]+dist(mid,R),rmax[rc]}

a n s [ p ] = m a x { a n s [ l c ] , a n s [ r c ] , r m a x [ l c ] + d i s t ( m i d , m i d + 1 ) + l m a x [ r c ] } ans[p]=max\{ans[lc],ans[rc],rmax[lc]+dist(mid,mid+1)+lmax[rc]\} ans[p]=max{ans[lc],ans[rc],rmax[lc]+dist(mid,mid+1)+lmax[rc]}

边界:当 L = R L=R L=R,设当前点为 i i i

i i i是白点

l m a x = r m a x = m a x { D i , 0 } lmax=rmax=max\{D_i,0\} lmax=rmax=max{Di,0}

a n s = m a x { 0 , D i , D i + D i ′ } ans=max\{0,D_i,D_i+D_i'\} ans=max{0,Di,Di+Di}

否则

l m a x = r m a x = D i lmax=rmax=D_i lmax=rmax=Di

a n s = D i + D i ′ ans=D_i+D_i' ans=Di+Di

最后需要维护 D i , D i ′ D_i,D_i' Di,Di

i i i是白点, j j j i i i的轻儿子

D i = m a x { 0 , w ( i , j ) + l m a x [ j ] } D_i=max\{0,w(i,j)+lmax[j]\} Di=max{0,w(i,j)+lmax[j]}

i i i是黑点

D i = w ( i , j ) + l m a x [ j ] D_i=w(i,j)+lmax[j] Di=w(i,j)+lmax[j]

然后用个堆之类的瞎维护即可

然而写起来十分精神污染

有一个常用的转换方式
在这里插入图片描述在这里插入图片描述
红色边权值为 0 0 0

这样这棵树变成了二叉树,且两两之间的距离不变。也就是说和原来的树是等效的。

有什么用呢?

这样一个节点最多有一个重儿子一个轻儿子, D i ′ D_i' Di永远为 − I N F -INF INF D i D_i Di可以直接计算,大大降低编程复杂度。当然好不好调是另外一回事

最后答案用 m u l t i s e t multiset multiset暴艹即可

复杂度 O ( n l o g n 2 ) O(nlog_n^2) O(nlogn2)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <set>
#define MAXN 200005
#define INF 0x3f3f3f3f
using namespace std;
inline int read()
{
	int ans=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9'){if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
	return f*ans;
}
inline char gal()
{
	char c=getchar();
	while (c<'A'||c>'Z') c=getchar();
	return c;
}
int n;
struct edge
{
	int u,v,w;
}e[MAXN];
int head[MAXN],nxt[MAXN],cnt;
void addnode(int u,int v,int w)
{
	e[++cnt]=(edge){u,v,w};
	nxt[cnt]=head[u];
	head[u]=cnt;
}
int son[MAXN][2];
int fa[MAXN],dep[MAXN],cost[MAXN],col[MAXN];
void dfs(int u)
{
	int las=u;
	for (int i=head[u];i;i=nxt[i])
		if (!dep[e[i].v])
		{
			fa[fa[e[i].v]=++n]=las;
			dep[e[i].v]=dep[u]+1;cost[e[i].v]=e[i].w;
			dfs(son[las=son[las][las!=u]=n][0]=e[i].v);
		}
}
int siz[MAXN],dis[MAXN],tp[MAXN],bt[MAXN];
int dfn[MAXN],pos[MAXN],tim;
void Dfs(int u)
{
	if (!u) return;
	siz[u]=1;
	Dfs(son[u][0]),Dfs(son[u][1]);
	siz[u]+=siz[son[u][0]]+siz[son[u][1]];
	if (siz[son[u][0]]<siz[son[u][1]]) swap(son[u][0],son[u][1]);
}
multiset<int> re;
int rt[MAXN],tot;
int ch[MAXN<<2][2],lmax[MAXN<<2],rmax[MAXN<<2],ans[MAXN<<2];
inline int d(const int& x)
{
	if (!son[x][1]) return col[x]? 0:-INF;
	if (col[x]) return max(cost[son[x][1]]+lmax[rt[son[x][1]]],0);
	return cost[son[x][1]]+lmax[rt[son[x][1]]];
}
#define lc ch[p][0]
#define rc ch[p][1]
inline void update(const int& p,const int& l,const int& r)
{
	int mid=(l+r)>>1;
	lmax[p]=max(lmax[lc],dis[pos[mid+1]]-dis[pos[l]]+lmax[rc]);
	rmax[p]=max(rmax[lc]+dis[pos[r]]-dis[pos[mid]],rmax[rc]);
	ans[p]=max(max(ans[lc],ans[rc]),rmax[lc]+cost[pos[mid+1]]+lmax[rc]);
}
void build(int& p,int l,int r)
{
	p=++tot;
	if (l==r)
	{
		if (col[pos[l]]) ans[p]=lmax[p]=rmax[p]=max(0,d(pos[l]));
		else lmax[p]=rmax[p]=d(pos[l]),ans[p]=-INF;
		return;
	}
	int mid=(l+r)>>1;
	build(lc,l,mid);build(rc,mid+1,r);
	update(p,l,r);
}
void modify(int p,int l,int r,int k)
{
	if (l==r)
	{
		if (col[pos[l]]) ans[p]=lmax[p]=rmax[p]=max(0,d(pos[l]));
		else lmax[p]=rmax[p]=d(pos[l]),ans[p]=-INF;
		return;
	}
	int mid=(l+r)>>1;
	if (k<=mid) modify(lc,l,mid,k);
	else modify(rc,mid+1,r,k);
	update(p,l,r);
}
void DFS(int u,int t)
{
	if (!u) return;
	dis[u]=dis[fa[u]]+cost[u];
	bt[tp[u]=t]=pos[dfn[u]=++tim]=u;
	DFS(son[u][0],t);DFS(son[u][1],son[u][1]);
	if (u==t) build(rt[u],dfn[u],dfn[bt[u]]),re.insert(ans[rt[u]]);
}
int main()
{
	freopen("test.in","r",stdin);
	n=read();
	int cnt=n;
	for (int i=1;i<=n;i++) col[i]=1;
	for (int i=1;i<n;i++)
	{
		int u,v,w;
		u=read(),v=read(),w=read();
		addnode(u,v,w),addnode(v,u,w);
	}
	dep[1]=1;
	dfs(1);Dfs(1);DFS(1,1);
	for (int i=read();i;--i)
	{
		if (gal()=='A') cnt? printf("%d\n",max(0,*re.rbegin())):puts("They have disappeared.");
		else
		{
			int x=read();
			for ((col[x]^=1)? ++cnt:--cnt;x;re.erase(re.find(ans[rt[tp[x]]])),modify(rt[tp[x]],dfn[tp[x]],dfn[bt[tp[x]]],dfn[x]),re.insert(ans[rt[tp[x]]]),x=fa[tp[x]]);
		}
	}
	return 0;
}

做这种题一定要先理清思路,弄懂定义再开始码,不要边想边写

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
洛谷的SPOJ需要注册一个SPOJ账号并进行绑定才能进行交题。您可以按照以下步骤进行注册: 1. 打开洛谷网站(https://www.luogu.com.cn/)并登录您的洛谷账号。 2. 在网站顶部导航栏中找到“题库”选项,将鼠标悬停在上面,然后选择“SPOJ”。 3. 在SPOJ页面上,您会看到一个提示,要求您注册SPOJ账号并进行绑定。点击提示中的接,将会跳转到SPOJ注册页面。 4. 在SPOJ注册页面上,按照要求填写您的用户名、密码和邮箱等信息,并完成注册。 5. 注册完成后,返回洛谷网站,再次进入SPOJ页面。您会看到一个输入框,要求您输入刚刚注册的SPOJ用户名。输入用户名后,点击“绑定”按钮即可完成绑定。 现在您已经成功注册并绑定了SPOJ账号,可以开始在洛谷的SPOJ题库上刷题了。祝您顺利完成编程练习!\[1\]\[2\] #### 引用[.reference_title] - *1* *3* [(洛谷入门系列,适合洛谷新用户)洛谷功能全解](https://blog.csdn.net/rrc12345/article/details/122500057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [luogu p7492 序列](https://blog.csdn.net/zhu_yin233/article/details/122051384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值