CSP2019 树上的树 口胡
Posted tztqwq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSP2019 树上的树 口胡相关的知识,希望对你有一定的参考价值。
拖了一年, 今天上午终于把这道题做出来了。
基本思路
题目要求字典序最小, 而字典序是有着天然的贪心性质的, 可以比较自然地想到要应用贪心算法, 进一步地, 要思考 “某个数字最终停留在某个点上” 会对全局的删边顺序产生哪些限制。
比如有这样一条路径: (s--^a--o--^b--o--^c--o--^d--t), 如果初始时点 s 上的数字最终停留在点 t, 那么一定满足:
- 边 a 是与点 s 相邻的边中第一个被删去的边
- 对于路径上的点 o(们), 在其相邻的边中, b 一定紧接着 a 被删去, c 一定紧接着 b 被删去
- 边 d 是与点 t 相邻的边中最后一个被删去的边
以上任意一条的任意部分不被满足, 初始时点 s 上的数字都不会最终停留在点 t; 以上所有条件都满足, 初始时点 s 上的数字最终就会停留在点 t。
发现以上的限制都是对于 "与某个点相邻的边" 之间的限制, 自然地认为要维护这个来辅助贪心算法的判断。
进一步的思路
有了基本的思路, 就可以思考出算法大致的框架了。
首先从小到大枚举数字, 找出在满足前面数字形成的限制下其可以到达的标号最小的节点, 然后把限制加上。
最朴素的找最小节点的思路就是枚举节点, 优化这个朴素思路的方法建立在如下事实上:
如果以当前数字的初始节点为根, 那么对于任意非根节点, 其与其父亲当作当前数字的最终节点所产生的限制是高度相似的
那么就可以通过 dfs 来查找当前枚举到的数字能够停留的标号最小的节点。
最终思路
仅剩的问题是如何维护与一个点相邻的边之间的相对顺序。
首先最终这些边的顺序一定是一个序列。
对于 “让这条边是这个点相邻边中 第一个/最后一个 被删除的边” 这种限制, 可以加哨兵, 这样就把所有的限制都转化成 “一个边要紧接着另一个边之后删” 了。
代码以及注释
(时间有限, 对于代码仅做了一些最基本的注释 仅供观赏)
#include<bits/stdc++.h>
using namespace std;
const int N = 2003;
int n, fa[N], p[N], deg[N];
int ct, hd[N], nt[N<<1], vr[N<<1];
void ad(int x,int y) {nt[++ct]=hd[x], hd[x]=ct, vr[ct]=y; }
int f[N][N], t[N][N], siz[N][N]; // 用于维护删边序列(链)的并查集, 一个集合的代表元就是删边序列的头部, 用 t 记录尾部, siz 记录序列长度
int fid(int *F, int x) {return F[x]==x ? x: F[x]=fid(F,F[x]); }
void mg(int x, int a, int b) { a=fid(f[x],a),b=fid(f[x],b); f[x][a]=b; t[x][b]=t[x][a]; siz[x][b]+=siz[x][a]; }
int bst; //这个变量用来记录当前数字能到达的标号最小的节点
void dfs(int x)
{ int ff=fid(f[x],fa[x]);
if(fa[x])
{
int ttt=fid(f[x],n+1);
if( !(t[x][ff]==0 && ttt==n+1 && siz[x][ff]+siz[x][ttt]!=deg[x]+2) )
if(ff!=ttt && t[x][ttt]==n+1 && ff==fa[x]) bst=min(bst,x);
}
for(int i=hd[x],y=vr[i];i;i=nt[i],y=vr[i]) if(y!=fa[x]) {
int tt=fid(f[x],y);
if(t[x][ff]==0 && tt==n+1 && siz[x][ff]+siz[x][tt]!=deg[x]+2) continue;
if( ff!=tt && ff==fa[x] && t[x][tt]==y) fa[y]=x,dfs(y);
}
}
int main()
{
int T; scanf("%d",&T);
while(T--)
{ scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&p[i]);
if(n==1) //特判一下
{ puts("1");
continue;
}
ct=0;
memset(hd,0,sizeof hd); memset(deg,0,sizeof deg);
for(int i=1,x,y; i<n; ++i)scanf("%d%d",&x,&y), ad(x,y),ad(y,x), ++deg[x],++deg[y];
for(int i=1;i<=n;++i)
for(int j=0;j<=n+1;++j)
f[i][j]=t[i][j]=j, siz[i][j]=1;
// 以上基本都是输入初始化
for(int i=1;i<=n;++i)
{ fa[p[i]]= 0; bst= n+2;
dfs(p[i]);
cout << bst << ‘ ‘;
int y=bst, x=fa[bst], z=n+1;
while(y)
{ mg(y,x,z);
z=y, y=x, x=fa[x];
}
}
putchar(‘
‘);
}
return 0;
}
以上是关于CSP2019 树上的树 口胡的主要内容,如果未能解决你的问题,请参考以下文章