正题

题目链接:https://www.luogu.com.cn/problem/P5659


题目大意

给出nnn个点的一棵树,每个节点上有一个数字,你每次可以选择一条边删除然后交换连接的两个点的数字,在删完所有数字后设pip_ipi​表示数字iii所在节点编号,要求使得排列ppp的字典序。

1≤n≤2000,1≤T≤101\leq n\leq 2000,1\leq T\leq 101≤n≤2000,1≤T≤10


解题思路

好阴间的题目

考虑对与一个点xxx的转移肯定是如下图类似的情况并且每个点连接的边的删除顺序是独立的(如果不独立证明出现了环,树上显然没有环)

考虑上图我们发现产生的数字搬运是x→1,1→2,2→3,3→xx\rightarrow 1,1\rightarrow 2,2\rightarrow 3,3\rightarrow xx→1,1→2,2→3,3→x,而边的删除顺序是(x,3)→(x,2)→(x,1)(x,3)\rightarrow (x,2)\rightarrow (x,1)(x,3)→(x,2)→(x,1)(红色箭头画反了)。不难发现的是因为必须删除所有边,所以最后肯定是xxx和它周围的节点连接形成一个搬运环。

然后因为字典序最小,所以考虑贪心,如果快速对于(s,t)(s,t)(s,t)判断sss上的数字能否搬运到ttt上。

先考虑在s→ts\rightarrow ts→t路径上(不包括s,ts,ts,t)的节点xxx需要满足的条件,记上一个节点为fafafa,下一个节点为yyy。

  • 如果有数字走y→xy\rightarrow xy→x或者x→fax\rightarrow fax→fa搬运过那么不行
  • 如果边x→yx\rightarrow yx→y或者fa→xfa\rightarrow xfa→x正反都搬运过数字那么不行
  • 如果此时对于节点xxx的边中加上fa→yfa\rightarrow yfa→y这条边后形成了一个环且不是所有xxx及其周围的数字都在环上的话那么显然不行。
  • 如果边(x,fa)(x,fa)(x,fa)已经要求在(x,y)(x,y)(x,y)之后再删除了那么显然不合法(也就是红色的箭头形成了环)

对于根节点和终止节点则类似只是减少了上面第444个要求因为显然这条边必须是最早/最晚删除的。

然后用类似链表的结构来维护每个节点连出去边的情况来判断后两个条件,前两个条件则很好判断。并且如果保证了对于每个节点xxx都满足它上面的数字不会搬回自己所在处就可以保证所有边必须删除了。

然后每次从未确定的最小的数字所在节点出发搜索所有合法的终止节点然后拿最小值再沿路更新即可。

时间复杂度:O(Tn2)O(Tn^2)O(Tn2)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2100;
struct node{int to,next;
}a[N<<1];
int T,n,tot,ls[N],p[N],d[N],d1[N],d2[N],fa[N];
int from[N],got[N],e[N][N],las[N][N],nxt[N][N];
bool v[N];
void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;d[y]++;return;
}
void dfs(int x,int root){for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa[x])continue;v[y]=1;if(x!=root){if(e[fa[x]][x]==fa[x]||e[x][y]==x)v[y]=0;//反向搬运过 if(e[fa[x]][x]==0||e[x][y]==0)v[y]=0;//被别的方向走过if(nxt[x][y]==from[x]&&las[x][fa[x]]==got[x]&&d[x]*2+d1[x]+d2[x]-2>0)v[y]=0;//构成一条偏序链  if(nxt[x][y]==fa[x])v[y]=0;//构成环 }else{if(e[x][y]==x)v[y]=0;//反向搬运过 if(e[x][y]==0)v[y]=0;//被别的方向走过if(from[x]&&nxt[x][y]==from[x]&&d[x]+d1[x]+d2[x]-1>0)v[y]=0;//构成一条偏序链 }v[y]&=v[x];fa[y]=x;dfs(y,root);}if(x==root)v[x]=0;else{if(from[x])v[x]=0;if(got[x]&&nxt[x][got[x]]==fa[x]&&d[x]+d1[x]+d2[x]-1>0)v[x]=0;}return;
}
int main()
{scanf("%d",&T);while(T--){scanf("%d",&n);tot=0;memset(d,0,sizeof(d));memset(ls,0,sizeof(ls));memset(d1,0,sizeof(d1));memset(d2,0,sizeof(d2));memset(got,0,sizeof(got));memset(from,0,sizeof(from));for(int i=1;i<=n;i++)scanf("%d",&p[i]);for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);addl(x,y);addl(y,x);e[x][y]=e[y][x]=-1;las[x][y]=nxt[x][y]=y;las[y][x]=nxt[y][x]=x;}for(int i=1;i<=n;i++){int x=p[i];for(int j=1;j<=n;j++)fa[j]=0;v[x]=1;dfs(x,x);int y=0;for(int j=1;j<=n;j++)if(v[j]){y=j;break;}printf("%d ",y);from[y]=fa[y];while(fa[y]!=x){if(e[fa[y]][y]==-1){e[fa[y]][y]=e[y][fa[y]]=fa[y];d[y]--;d2[y]++;d[fa[y]]--;d1[fa[y]]++;}else{e[fa[y]][y]=e[y][fa[y]]=0;d1[y]--;d2[fa[y]]--;}int z=y;y=fa[y];las[y][nxt[y][z]]=las[y][fa[y]];nxt[y][las[y][fa[y]]]=nxt[y][z];//数字fa->y->z,所以y->fa的连接y->z }if(e[fa[y]][y]==-1){e[fa[y]][y]=e[y][fa[y]]=fa[y];d[y]--;d2[y]++;d[fa[y]]--;d1[fa[y]]++;}else{e[fa[y]][y]=e[y][fa[y]]=0;d1[y]--;d2[fa[y]]--;}got[x]=y;}putchar('\n');}return 0;
}

P5659-[CSP-S2019]树上的数【贪心】相关推荐

  1. HDU 6178 Monkeys (匹配数+贪心)*

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6178 #include<bits/stdc++.h> using namespace st ...

  2. 选择有限时间内最多的活动数 贪心

    题目: 有n个需要在同一天使用同一个教室的活动a1,a2,-,an,教室同一时刻只能由一个活动使用. 每个活动ai都有一个开始时间si和结束时间fi .一旦被选择后,活动ai就占据半开时间区间[si, ...

  3. #CSP 201312-4 有趣的数

    问题描述 我们把一个数称为有趣的,当且仅当: 1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次. 2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前. 3. 最高 ...

  4. CSP 201312-4 有趣的数

    题目链接 典型的数位DP~ 打表易发现所有符合条件的数都是以 2 2 2 开头的,那么一共有以下几种状态: 只包含数字 2 只包含数字 2,0 只包含数字 2,3 只包含数字 2,0,3 只包含数字 ...

  5. CSP认证201409-1 相邻数对[C++题解]:排序

    文章目录 题目解答 题目链接 题目解答 来源:acwing 分析: 排序,遍历即可. ac代码 #include<bits/stdc++.h> using namespace std; c ...

  6. csp试题2:小明种苹果(绪)

    csp试题2:小明种苹果(绪) 题目 分析 代码 总结 题目 题目描述       小明在他的果园里种了一些苹果树,这些苹果树排列成一个圆.为了保证苹果的品质,在种植过程中要进行疏果操作.为了更及时地 ...

  7. 动态规划和贪心算法的区别

    要想清楚两者的区别,首先就要了解两者的基本概念与性质. 1. 贪心算法 基本思想:贪心算法并不从整体最优上加以考虑,它所做的选择只是在某种意义上的局部最优解. 基本要素:最优子结构性质和贪心选择性质. ...

  8. (转)动态规划和贪心算法的区别

    (转自)http://hi.baidu.com/35661327/blog/item/d5463e17f1e8d011972b439c.html 动态规划和贪心算法的区别 2009-07-27 13: ...

  9. 贪心法和动态规划法的区别

    动态规划和贪心算法的区别 动态规划和贪心算法都是一种递推算法  均用局部最优解来推导全局最优解 不同点:  贪心算法:  1.贪心算法中,作出的每步贪心决策都无法改变,因为贪心策略是由上一步的最优解推 ...

最新文章

  1. oracle绑定变量赋值,Oracle教程之绑定变量
  2. 去掉字符串左右的空格
  3. linux交换分区的文件格式为,LINUX的交换分区或交换文件SWAP的查看与维护
  4. 萨义德与巴伦博依姆关于音乐和文学的对话
  5. python实现手机号归属地相关信息查询
  6. jquery ajax post请求连续多个问号特殊数据异常问题
  7. 【OpenCV 例程200篇】74. 图像的抗混叠
  8. text文字垂直居中_CSS垂直居中,你会多少种写法?
  9. 绿茶2003服务器系统 新浪,我在用WIN server2003
  10. 解析JavaScript模拟事件的注意要点
  11. UIFont各种字体
  12. 数字图像处理学习笔记(一)——数字图像处理概述
  13. 转置卷积(Transposed Convolution)
  14. word-break:break-word、word-break:break-all和word-wrap:break-word 自动换行
  15. c语言统计英文字母频率,C语言实现英文文本词频统计
  16. 摄像头通过服务器和显示器连接,网络摄像头能直接和显示器接吗
  17. 街景窗户检测数据集(Street Scene Window Detection (SSWD))-VOC和Yolo版本
  18. 《Java黑皮书基础篇第10版》 第7章【习题】
  19. java outputstream下载_java – Spring OutputStream – 用IE下载pptx
  20. Shiro学习笔记(三)源码解析

热门文章

  1. java中可以用浮点作为循环变量吗_Java千问:Java循环语句的几个冷门知识点你都知道吗?...
  2. linux 修改默认脚本,linux环境初始脚本
  3. php 自定义菜单 openid,微信公众平台开发(99) 自定义菜单获取OpenID
  4. html点击按钮计算两个输入框的和_小程序计算报价功能介绍
  5. python os system_python中os. popen system的区别
  6. Java并发之volatile
  7. 手机键鼠映射软件_吃鸡,我最专业!---盖世小鸡键鼠吃鸡套装评测
  8. [计组]压缩BCD码指二进制编码的十进制
  9. [Java基础]Set集合概述和特点
  10. C++变量的初始化问题及列表初始化