ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【洛谷5659】[CSP-S2019] 树上的数(思维)

2021-05-07 13:32:24  阅读:225  来源: 互联网

标签:一条 洛谷 ee 第一条 链表 5659 删去 S2019 define


点此看题面

  • 给定一棵\(n\)个点的树,每个点上有一个数字。
  • 你可以按任意顺序删去树上的所有边,每当要删去一条边,这条边两端点上的数字将会交换。
  • 记录\(P_i\)表示最终\(i\)所在节点,求字典序最小的\(P\)。
  • 数据组数\(\le10,n\le2000\)

同一点的边

这道题的核心问题就在于删边顺序,因此我们首先要尝试简化问题,发现只需考虑同一点的边之间的删除顺序。

因为显然不同点的边删除顺序是无用信息,而知道了所有点各自边之间的删除顺序又一定能构造出一个符合条件的方案,所以这样做是完全可行的。

而要维护同一点的边的删除顺序,我们可以考虑用链表进行处理。

字典序问题的贪心

这种最小化字典序的问题有一个经典贪心,就是我们依次枚举每一位,尽可能填入能填的最小数。

所以我们的核心问题在于如何判定在当前情况下,是否可能让\(x\)上的数字最终留在\(y\)上。

找到\(x\)到\(y\)这条路径,它可以分为三部分:起点\(x\)、中间若干点、终点\(y\)。接下来就是分类讨论。

三部分的分类讨论

对于起点\(x\),考虑它连出的起始边。显然起始边必须是\(x\)的边当中第一条被删去的边(因此我们需要记录每条边是否是第一条被删去的,以及每个点第一条删去的是谁),而一条边可以被第一条删去需要满足它在链表中没有前驱。

对于终点\(y\),考虑它连出的终止边。显然终止边必须是\(y\)的边当中最后一条被删去的边(因此我们还需要记录每条边是否是最后一条被删去的,以及每个点最后一条删去的是谁),而一条边可以被最后一条删去需要满足它在链表中没有后继。

现在我们既需要记录第一条被删去的边\(H\),又需要记录最后一条被删去的边\(T\),这时候就会产生一个坑点:如果\(H\)和\(T\)在链表中的一条链上,则这条链长必须等于这个点的度数,因为所有边都必须被删去(因此我们需要在每条链的左端点上记录链长,并对于每个端点记下它的对面端点,方便判断这条链是否变成了以\(H\)开头以\(T\)结尾)。

所以想把一条边设为第一条边还需要注意若它所在链的右端点是最后一条边,则这条链长必须等于度数(想设成最后一条边同理需要类似判断)。

对于中间的点,考虑到达它的边\(p\)和离开它的边\(q\),那么我们相当于想要在链表上连接\(p,q\),因此需要满足\(p\)不是最后一条边、\(q\)不是第一条边、\(p\)没有后继、\(q\)没有前驱、\(p,q\)不在同一条链中,且如果\(p\)是第一条边、\(q\)是最后一条边还需要满足两链长之和恰好为度数。

一个点出发的答案

然后我们发现如果枚举\(x,y\)再判断\(x\)上的数能否留在\(y\)上,还需要一次\(O(n)\)的判断,会挂掉。

但实际上,从\(x\)出发到任意\(y\),中间点的讨论都是类似的。

因此,对于起点\(x\)我们选择可能的起始边出发,每到一个点先尝试把连向它的边作为终止边,若可以则更新答案。然后把这个点当作中间点,枚举出边尝试转移。

这样一来,对于每个起点只需遍历一次就可以了。

找到最优终点之后,一路往回跳更新沿途所有中间点,并特殊更新起点和终点即可。

代码:\(O(Tn^2)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 2000
#define add(x,y) (++d[x],e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,s[ee]=node(ee))
using namespace std;
int n,p[N+5],d[N+5],H[N+5],T[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
struct node {int pre,nxt,H,T,L,R,C;I node(CI x=0) {pre=nxt=H=T=0,L=R=x,C=1;}}s[N<<1];
int ans,lst[N+5];I void dfs(CI x)
{
	RI p=lst[x]^1;(!T[x]||T[x]==p)&&!s[p].nxt&&(!s[s[p].L].H||s[s[p].L].C==d[x])&&(ans=min(ans,x));//尝试作为终点
	for(RI q=lnk[x];q;q=e[q].nxt) e[p].to^e[q].to&&T[x]^p&&H[x]^q&&!s[p].nxt&&!s[q].pre&&
		s[p].L^s[q].L&&(!s[s[p].L].H||!s[s[q].R].T||s[s[p].L].C+s[q].C==d[x])&&(lst[e[q].to]=q,dfs(e[q].to),0);//作为中间点,枚举边尝试转移
}
I void Mark(CI x,RI y)//让x上的数留在y上
{
	RI p,q=lst[y]^1;s[T[y]=q].T=1,y=e[q].to;//特殊更新终点
	W(x^y) p=lst[y]^1,q^=1,s[p].nxt=q,s[q].pre=p,//更新中间点
		s[s[p].L].C+=s[q].C,s[s[p].L].R=s[q].R,s[s[q].R].L=s[p].L,y=e[q=p].to;//合并链信息
	s[H[x]=q^1].H=1;//特殊更新起点
}
int main()
{
	RI Tt,i,x,y;scanf("%d",&Tt);W(Tt--)
	{
		for(scanf("%d",&n),ee=i=1;i<=n;++i) scanf("%d",p+i),lnk[i]=d[i]=H[i]=T[i]=0;//清空
		for(i=1;i^n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);if(n==1) {puts("1");continue;}//特判n=1
		for(y=1;y<=n;printf("%d ",ans),Mark(x,ans),++y) for(ans=n,i=lnk[x=p[y]];i;i=e[i].nxt)
			(!H[x]||H[x]==i)&&!s[i].pre&&(!s[s[i].R].T||s[i].C==d[x])&&(lst[e[i].to]=i,dfs(e[i].to),0);//枚举起始边
		putchar('\n');
	}return 0;
}

标签:一条,洛谷,ee,第一条,链表,5659,删去,S2019,define
来源: https://www.cnblogs.com/chenxiaoran666/p/Luogu5659.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有