P3714 [BJOI2017]树的难题 题解

我太蒻了,又调了一天……

本题如果没有 l,r 的限制,就是一个裸的点分治。

现在我们加上了 l,r 的限制条件,那么我们就需要进行区间处理。

如果在范围内进行 01 背包,很明显有单调性,但是由于有排序等操作,复杂度会上升到 O(n×log22(n)),而我太菜不太会 bfs 优化,那既然复杂度都是 log22(n),为什么不用线段树呢。

对于每一个点,分裂时按照其与子节点的边的颜色进行排序,然后在每次对于一个子树有 k 条边的情况,那么另一条分支应该是之前子树中有 (max(1,lk),rk) 区间中的边所得到答案的最大值。

如果合并时边相同,要减去 c(i,j)

所以要维护两个线段树,一个代表与自己颜色不同,一个代表与自己颜色相同

所以大概步骤是这样的:

1、处理每一棵子树的每个深度所得到答案最大值。

2、对所有子树按颜色进行排序。

3、枚举一棵子树的深度,利用线段树求得另一棵子树合法的长度能得到答案的最大值,如果颜色相等要减去根到子节点的颜色所代表的权值。

4、更新同种颜色的线段树。

5、若这个颜色枚举完了,将同种颜色的线段树归并到不同种颜色线段树上,并清空同种颜色线段树。

然后是点分治的最重要的,清空数组。

为方便清空,在进行排序时,会将同种颜色的按深度排序。

那么子树每个深度的最大答案就可以利用深度进行清空,而线段树归并时同样深度也是最后一个树的深度。

然后其他都不是特别难,除了线段树。

线段树建树来清空肯定会 T,所以在每个节点第一次更新后,维护一个栈或队列记录这个点,最后要清空时依次弹出即可。

时间复杂度:O(n×log22(n))

#include<iostream>
#include<cstdio>
#include<stack>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int N=2e5+5;
int n,m,l,r,t[N],mp[N],siz[N],root;
int vis[N],tot,ans=-2000000000,dep[N],c[N],tmp[N],cnt,nl,nr,k,dis[N];
struct segmentree
{
	int f[4*N];
	stack<int>s; 
	inline int ls(int x)
	{
		return x<<1;
	}
	inline int rs(int x)
	{
		return x<<1|1;
	}
	inline void pushup(int x)
	{
		if(f[x]==-2000000000)s.push(x);
		f[x]=max(f[ls(x)],f[rs(x)]);
	}
	void build(int x,int l,int r)
	{
		f[x]=-2000000000;
		if(l==r)return;
		int mid=(l+r)>>1;
		build(ls(x),l,mid);
		build(rs(x),mid+1,r);
		pushup(x);
	}
	void update(int x,int l,int r)
	{
		if(l==r)
		{
			if(f[x]==-2000000000)s.push(x); 
			f[x]=max(f[x],k);
			return;
		}
		int mid=(l+r)>>1;
		if(mid>=nl)update(ls(x),l,mid);
		else update(rs(x),mid+1,r);
		pushup(x);
	}
	int search(int x,int l,int r)
	{
		if(l>=nl&&r<=nr)return f[x];
		int mid=(l+r)>>1,num=-2000000000;
		if(mid>=nl)num=max(num,search(ls(x),l,mid));
		if(mid<nr)num=max(num,search(rs(x),mid+1,r));
		return num;
	}
	void clear()
	{
		while(!s.empty())
		{
			f[s.top()]=-2000000000;
			s.pop();
		}
	}
}t1,t2;
struct node
{
	int to,data;
};
vector<node>a[N];
int cmp(int x,int y)
{
	if(c[x]==c[y])return dep[x]<dep[y];
	return c[x]<c[y];
}
void findzx(int x,int fa)
{
	mp[x]=0;
	siz[x]=1;
	int len=a[x].size();
	for(int i=0;i<len;i++)
	{
		if(vis[a[x][i].to]||a[x][i].to==fa)continue;
		findzx(a[x][i].to,x);
		siz[x]+=siz[a[x][i].to];
		mp[x]=max(mp[x],siz[a[x][i].to]);
	}
	mp[x]=max(mp[x],tot-siz[x]);
	if(mp[x]<mp[root])root=x;
}
void dfs(int x,int fa)
{
	int len=a[x].size();
	dep[x]=1;
	for(int i=0;i<len;i++)
	{
		if(vis[a[x][i].to]||a[x][i].to==fa)continue;
		dfs(a[x][i].to,x);
		dep[x]=max(dep[x],dep[a[x][i].to]+1);
	}
}
void getdis(int x,int fa,int num,int deep,int sum)
{
	dis[deep]=max(dis[deep],sum);
	int len=a[x].size();
	for(int i=0;i<len;i++)
	{
		if(a[x][i].to==fa||vis[a[x][i].to])continue;
		int lim=sum;
		if(a[x][i].data!=num)lim+=t[a[x][i].data];
		getdis(a[x][i].to,x,a[x][i].data,deep+1,lim);
	}
}
void clac(int x)
{
	int len=a[x].size();
	cnt=0;
	for(int i=0;i<len;i++)
	{
		if(vis[a[x][i].to])continue;
		c[a[x][i].to]=a[x][i].data;
		dfs(a[x][i].to,x);
		tmp[++cnt]=a[x][i].to;
	}
	sort(tmp+1,tmp+1+cnt,cmp);
	for(int i=1;i<=cnt;i++)
	{
		dis[1]=t[c[tmp[i]]];
		getdis(tmp[i],x,c[tmp[i]],1,dis[1]);
		for(int j=1;j<=dep[tmp[i]];j++)
		{
			if(j>=l&&j<=r)ans=max(ans,dis[j]);
			nl=l-j,nr=r-j;
			if(nl<=0)nl=1;
			int num1=-2000000000,num2=-2000000000;
			if(nl<=nr)num1=t1.search(1,1,n);
			if(nl<=nr)num2=t2.search(1,1,n);
			ans=max(ans,max(num1+dis[j],num2+dis[j]-t[c[tmp[i]]]));
		}
		for(int j=1;j<=dep[tmp[i]];j++)
		{
			nl=j,k=dis[j];
			t2.update(1,1,n);
			dis[j]=-2000000000;
		}
		if(i!=cnt&&c[tmp[i]]!=c[tmp[i+1]])
		{
			for(int j=1;j<=dep[tmp[i]];j++)
			{
				nl=j,nr=j;
				k=t2.search(1,1,n);
				t1.update(1,1,n);
			}
			t2.clear();
		}
	}
	t1.clear();
	t2.clear();
}
void divide(int x,int num)
{
	vis[x]=1;
	clac(x);
	int len=a[x].size();
	for(int i=0;i<len;i++)
	{
		if(vis[a[x][i].to])continue;
		if(siz[x]>siz[a[x][i].to])tot=siz[a[x][i].to];
		else tot=num-siz[x];
		root=0;
		findzx(a[x][i].to,x);
		divide(root,tot);
	}
}
int main()
{
	//freopen("journey1.in","r",stdin);
	scanf("%d%d%d%d",&n,&m,&l,&r);
	for(int i=1;i<=m;i++)scanf("%d",&t[i]);
	for(int i=1;i<n;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w); 
		a[u].push_back({v,w});
		a[v].push_back({u,w});
	}
	tot=n;
	mp[0]=2e9;
	t1.build(1,1,n);
	t2.build(1,1,n);
	findzx(1,0);
	for(int i=1;i<=n;i++)dis[i]=-2000000000;
	divide(root,n);
	printf("%d",ans);
	return 0;
}
posted @   Gmt丶Fu9ture  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示