HDU5764 After a Sleepless Night 树形乱搞题

分析(官方题解):

假设根已确定,可以发现新树若合法,需满足以下性质:根节点是n;儿子的值不大于父亲;具有相同值的节点形成一条链,并且链不会发生“分叉”(即有多个最低点)。所以对于新树中有出现的值x,原树在新树x链的最低点应为x,而其他新值为x的点,原值应小于x。那么我们先将所有链的最低点放上对应值,而空着的点和还没用的值进行配对。 贪心使答案字典序最小:从大到小枚举未用的值,用大根堆维护该值可以填入的位置id,取最大id填入。(否则,若某一步的值a匹配了非最大id x,而最大id y在后面配了值b,那么交换xy将产生更优解)。 还有一种方法是从大到小枚举空位置,填入 最接近小于 该位置新值的 未用的值。这用set是容易实现的。并且队友想出了使用并查集+双向链表的写法,可做到并查集复杂度(O(n*a(n)))。 不合法的情况除了不满足上述性质,还有就是匹配的过程中出现值或位置不够用。

然后考虑根的选择。由上所述,根的值一定是新树n链的两个端点之一。可以发现,无论选哪个做根,不影响其它链的上下方向及链之间的相对关系,即不影响合法性。因为没选的那一头一定填n,所以我们贪心地选择id小的端点做根(否则交换两个端点的值,将得到更优的答案)。当然也可以两个点都跑一遍。 (出题人写hint的时候,把自己绕晕了。不好意思。)

一点感想:这个题就是说原树是有根树(每个点有权值),新树每个节点的权值是其子树的最大值

由于是排列(也就是每个权值都不一样),所以只有链状,具体的分析见这一篇

http://blog.csdn.net/bblss123/article/details/52058959

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
typedef  long long LL;
const int N = 1e5+5;
int head[N],tot;
struct Edge{
  int v,next;
}edge[N<<1];
void add(int u,int v){
   edge[tot].v=v;
   edge[tot].next=head[u];
   head[u]=tot++;
}
int val[N],ret[N],d[N],n,T,color[N],fa[N],kase;
vector<int>s;
bool can[N];
bool dfs(int u){
  int sp=0;
  for(int i=head[u];~i;i=edge[i].next){
     int v=edge[i].v;
     if(v==fa[u])continue;
     if(val[v]>val[u])return false;
     else if(val[v]==val[u]){if((++sp)>1)return false;}
     fa[v]=u;
     if(!dfs(v))return false;
  }
  if(!sp){color[val[u]]=u;ret[u]=val[u];can[val[u]]=false;}
  return true;
}
bool solve(){
   s.clear();scanf("%d",&n);tot=0;
   for(int i=1;i<=n;++i)color[i]=head[i]=-1,fa[i]=d[i]=0,can[i]=true;
   for(int i=1;i<=n;++i){
    scanf("%d",&val[i]);
    if(val[i]==n)s.push_back(i);
   }
   for(int i=1;i<n;++i){
      int u,v;scanf("%d%d",&u,&v);
      add(u,v);add(v,u);
      if(val[u]==n&&val[v]==n)++d[u],++d[v];
   }
   if(!s.size())return false;
   for(int i=1;i<s.size();++i)
     if(d[s[i]]<d[s[0]]||d[s[i]]==d[s[0]]&&s[i]<s[0])
        swap(s[i],s[0]);
   if(d[s[0]]>1)return false;
   if(!dfs(s[0]))return false;
   priority_queue<int>q;
   for(int i=n;i>0;--i){
     if(can[i]){
        if(q.empty())return false;
        ret[q.top()]=i;q.pop();
     }
     if(color[i]!=-1){
        int tmp=color[i];
        while(val[fa[tmp]]==i){
           q.push(fa[tmp]);
           tmp=fa[tmp];
        }
     }
   }
  return true;
}
int main(){
  scanf("%d",&T);
  while(T--){
    printf("Case #%d:",++kase);
    if(!solve())printf(" Impossible\n");
    else {
      for(int i=1;i<=n;++i)printf(" %d",ret[i]);
      printf("\n");
    }
  }
  return 0;
}

时间: 2024-10-12 23:31:03

HDU5764 After a Sleepless Night 树形乱搞题的相关文章

codeforces 653C C. Bear and Up-Down(乱搞题)

题目链接: C. Bear and Up-Down time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output The life goes up and down, just like nice sequences. Sequence t1, t2, ..., tn is called nice if the following two

一道乱搞题并不能ac.

题目描述 传销组织 传销组织 GPLT的 宗旨是"有志者事竟成",他们最近在执行一项宏伟的 宗旨是"有志者事竟成",他们最近在执行一项宏伟的 宗旨是"有志者事竟成",他们最近在执行一项宏伟的 宗旨是"有志者事竟成",他们最近在执行一项宏伟的 宗旨是"有志者事竟成",他们最近在执行一项宏伟的 宗旨是"有志者事竟成",他们最近在执行一项宏伟的 宗旨是"有志者事竟成",他们

hdu-5676 ztr loves lucky numbers(乱搞题)

题目链接: ztr loves lucky numbers Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) 问题描述 ztr喜欢幸运数字,他对于幸运数字有两个要求 1:十进制表示法下只包含4.7 2:十进制表示法下4和7的数量相等 比如47,474477就是 而4,744,467则不是 现在ztr想知道最小的但不小于n的幸运数字是多少 输入描述 有TT(1≤T≤10?5??)组数据,

hihocoder 1236(2015北京网络赛 J题) 分块bitset乱搞题

题目大意: 每个人有五门课成绩,初始给定一部分学生的成绩,然后每次询问给出一个学生的成绩,希望知道在给定的一堆学生的成绩比这个学生每门都低或者相等的人数 因为强行要求在线查询,所以题目要求,每次当前给定的学生成绩都异或上一次的答案 先将学生按每一门成绩都排一次序 这里将学生分块成sqrt(n)的块数,然后在当前块中用bitset容器来记录含有学生的状态 这里可以记录状态的前缀和,因为比后面成绩好的,必然比前面的学生的成绩也好 查询的时候只要查到正好比他高的学生属于哪一块,这样只要访问sqrt(n

UVA 11853 [dfs乱搞]

/* 大连热身E题 不要低头,不要放弃,不要气馁,不要慌张 题意: 在1000×1000的格子内有很多个炮弹中心,半径给定. 为某人能否从西部边界出发,从东部边界走出. 不能输出不能,能的话输出最北边的入口和出口的坐标. 思路: dfs乱搞题.把炮弹辐射范围连在一起的炮弹看作一个整体,记录下它围起来的边界区域. 然后找到最北边的输出. */ #include<bits/stdc++.h> using namespace std; double x[1005],y[1005],r[1005];

hdu 5246 乱搞

题意:题目太长直接看链接 链接:点我 乱搞题 显然,一个人要想成功,必须大于等于最强的人的战斗力,所以我们从后往前看 这里直接拿例1解释,首先递减排个序 15,13,10,9,8 作差得2,3,1,1, 此时我们从10出发即可成功 同时也发现,战斗力逐渐递增和直接到某个值其实是等价的 于是我们假设战斗力是从15-13-10-9-8变化的,观察这种变化能否成功即可 由13到15,变化为2,则从13出发剩余战斗力至少得提高2 从10到13,战斗力要提高3,而k为3然后10小于m,即成功 看一下反例2

CodeForces - 1228D (暴力+思维+乱搞)

题意 https://vjudge.net/problem/CodeForces-1228D 有一个n个顶点m条边的无向图,在一对顶点中最多有一条边. 设v1,v2是两个不相交的非空子集,当满足以下条件时f(v1,v2)为真 v1中的点之间不存在边 v2中的点之间不存在边 对于在v1v2中的每一对顶点,x在v1中,y在v2中,xy之间有边 所有点集不为空,且不相交,是否有v1,v2,v3使得f(v1,v2).f(v2,v3).f(v3,v1)均为真 如果有输出每个点所在的点集(1,2,3),否则

Codeforces 732e [贪心][stl乱搞]

/* 不要低头,不要放弃,不要气馁,不要慌张 题意: 给n个插座,m个电脑.每个插座都有一个电压,每个电脑都有需求电压. 每个插座可以接若干变压器,每个变压器可以使得电压变为x/2上取整. 有无限个变压器供应. 问最多能使得多少个插座与电脑匹配,使得电压一致. 如果有多种方案,输出需要变压器总数最小的那种. 输出匹配数量 输出每个插座需要接多少个变压器.输出每台电脑匹配哪个插座. 思路: 贪心 乱搞 先从小到大将插座排序,然后从地第一个插座开始,不断除以2上取整.不断找是否可以匹配.找到匹配就停

学渣乱搞系列之dp斜率优化

学渣乱搞系列之dp斜率优化 By 狂徒归来 貌似dp的斜率优化一直很难搞啊,尤其是像我这种数学很挫的学渣,压根不懂什么凸包,什么上凸下凸的,哎...说多了都是泪,跟wdd讨论了下,得出一些结论.本文很大部分参考了大神Accept的文章,不过此神貌似早已绝迹江湖,这篇文章写得好,也写得很差,前半部分叙述得很好,可是关键,关键部分说得很乱,有些许错误,很多大神都进行了评论指出,但是大神Accept貌似没有修改的意思,故重新总结下,以便自己以后查阅和复习啊. 下面看一个例题Print Article.