专栏文章

CF2107D

CF2107D题解参与者 1已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@mint0fih
此快照首次捕获于
2025/12/02 07:52
3 个月前
此快照最后确认于
2025/12/02 07:52
3 个月前
查看原文

思路

考虑用一个类似于点分治的方法写。
思路是显然的,考虑到是求字典序最大,所以路径长度一定是最重要的,其次是端点坐标。
所以可以考虑树的直径。
然后每次通过树形动态规划找到最大序号的直径,然后删除,再对剩下的子树去做。
复杂度是 O(NlogN)O(N\log N) 的。
难点在于代码。

代码

CPP
#include<bits/stdc++.h>
using namespace std;
const int N=150005;
struct G
{
	int ne,to;
}e[2*N];
struct T
{
	int d,x,y;
}ans[N];
int he[N],idx,a[N],f[N],u[N],v[N],c[N],top,sz,fat[N],vis[N],g[N],mxid[N];
void add(int x,int y)//加边
{
	e[++idx].ne=he[x];
	e[idx].to=y;
	he[x]=idx;
}
void dfs(int x,int fa)//树形dp
{
	fat[x]=fa;//记录祖先
	c[++top]=x;//块内元素
	f[x]=0;//最长路径长度
	g[x]=0;//最长的以当前根为起点的路径长度
	mxid[x]=x;//能取到g[x]的另一个端点的id最大值
	int mx=0,cmx=0,id=x,cid=x;//最长的以当前根为起点的路径长度、次最长的以当前根为起点的路径长度、能取到mx的另一个端点的id最大值、能取到cmx的另一个端点的id最大值
	for(int i=he[x];i;i=e[i].ne)//先取最长路径
	{
		int y=e[i].to;
		if(a[y]||y==fa)continue;
		dfs(y,x);
		if(g[y]>mx||(g[y]==mx&&mxid[y]>mxid[id]))
		{
			mx=g[y];
			id=y;
		}
	}
	for(int i=he[x];i;i=e[i].ne)//再取次长路径
	{
		int y=e[i].to;
		if(a[y]||y==fa||y==id)continue;
		if(g[y]>cmx||(g[y]==cmx&&mxid[y]>mxid[cid]))
		{
			cmx=g[y];
			cid=y;
		}
	}
	f[x]=mx+cmx+1;
	g[x]=mx+1;
	u[x]=max(mxid[id],mxid[cid]);//具体构造方案(f[x],u[x],v[x])
	v[x]=min(mxid[id],mxid[cid]);
	mxid[x]=mxid[id];
}
int get(int x)//得到当前联通块答案
{
	top=0;
	dfs(x,0);
	int mx=0,id=x;
	for(int i=1;i<=top;i++)//保证复杂度
	{
		int y=c[i];
		if(f[y]>mx||(f[y]==mx&&(u[y]>u[id]||(u[y]==u[id]&&v[y]>v[id]))))
		{
			id=y;
			mx=f[y];
		}
	}
	return id;
}
void run(int x)//分治,处理路径
{
	x=get(x);
	for(int i=1;i<=top;i++)vis[c[i]]=0;
	ans[++sz]={f[x],u[x],v[x]};
	int s=u[x],t=v[x],lca;//计算lca
	while(s)
	{
		vis[s]=1;
		s=fat[s];
	}
	while(t)
	{
		if(vis[t])
		{
			lca=t;
			break;
		}
		t=fat[t];
	}
	s=u[x];t=v[x];a[lca]=1;//删除路径
	while(s!=lca)
	{
		a[s]=1;
		s=fat[s];
	}
	while(t!=lca)
	{
		a[t]=1;
		t=fat[t];
	}
	s=u[x];t=v[x];//分治
	int lst=0;
	while(lst!=lca)
	{
		for(int i=he[s];i;i=e[i].ne)
		{
			int y=e[i].to;
			if(a[y])continue;
			run(y);
		}
		lst=s;
		s=fat[s];
	}
	while(t!=lca)
	{
		for(int i=he[t];i;i=e[i].ne)
		{
			int y=e[i].to;
			if(a[y])continue;
			run(y);
		}
		t=fat[t];
	}
}
bool cmp(T a,T b)//对每个联通块答案排序
{
	if(a.d!=b.d)return a.d>b.d;
	if(a.x!=b.x)return a.x>b.x;
	return a.y>b.y;
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			he[i]=0;
			a[i]=0;
		}
		idx=0;
		for(int i=1;i<n;i++)
		{
			int x,y;
			cin>>x>>y;
			add(x,y);
			add(y,x);
		}
		sz=0;
		run(1);
		sort(ans+1,ans+sz+1,cmp);
		for(int i=1;i<=sz;i++)cout<<ans[i].d<<' '<<ans[i].x<<' '<<ans[i].y<<' ';
		cout<<'\n';
	}
	return 0;
}

评论

1 条评论,欢迎与作者交流。

正在加载评论...