[HDU5764] After a Sleepless Night [2016 Multi-University Training Contest 4(2016多校联合训练4) B]

比赛rank15,第一次这么靠前诶


题意

一棵节点数为 n 的树,树节点编号1n,每个节点也有一个权值 1n ,且互不相同。现用这棵树构建一棵新树,节点间连边不变,新图中每个节点的权值变为原图中该节点为根的子树中权值的最大值。
给出这棵新树的描述,问是否可以还原为原树?输出原图节点 1n 的权值,若有多种答案输出字典序最小的答案,无答案输出 Impossible

题解

考虑原图变为新图后新图的性质。

  1. 权值 n 一定出现在新图中
  2. 新图中每种权值必然连成一条链
  3. 新图中从根开始向下遍历遇到的权值一定是非严格递减

于是这三条性质成为我们判断Impossible的一部分条件。

读入数据后先判定是否存在权值 n 。根据性质1

找到权值 n 所在的链,新树的根必然是这条链的两个端点之一。可以发现根的选择不影响其它链的上下方向及链之间的相对关系。而根的另一头一定填n,所以我们贪心地选择编号小的端点做根(否则交换两端点的值,会得到更优解)

下面从根开始 dfs ,遍历时记录一下 father

遍历到一个节点 u 时:

如果u的权值和 fa[u] 相等,则 chain[fa[u]]++ ,代表 fa[u] 的权值向下的分叉多了一个,当 chain[fa[u]]>1 时,根据性质 2 Impossible
如果 u 的权值大于fa[u]的权值,根据性质 3 Impossible

遍历 u 的儿子后返回

如果chain[u]=0说明 u 是它的权值的链中最靠下的点,它权值就是确定的了,即为当前的权值。

跳出dfs开始考虑填入剩下的权值。

n 开始到1枚举权值:

如果当前权值已经被确定位置,即为某一条权值链的最下端,则取出这条权值链中其它节点的位置,存到一个大根堆中。注意这些位置对之后枚举的权值都是可行的位置,因为是从大到小枚举的权值。
如果当前权值未被确定位置,则从大根堆中取出堆顶,将此权值放入取出的位置上。这个地方是贪心使得字典序最小,若当前权值不使用这个最大的位置,通过交换当前权值和这个位置上的权值可以得到更优解。
如果当前权值未被确定位置,而堆空了,则 Impossible

代码

// team646
// by ztx
// 2016-07-28 Multi University 1002
#include <cstdio>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}
template <typename TP>inline void readc(TP& ret) {
    while (ret=getchar() , ret<'!') ;
    while (CH=getchar() , CH>'!') ;
}
template <typename TP>inline void reads(TP *ret) {
    ret[0]=0;while (CH=getchar() , CH<'!') ;
    while (ret[++ret[0]]=CH,CH=getchar(),CH>'!') ;
    ret[ret[0]+1]=0;
}

#include <algorithm>
#include <queue>

#define  maxn  100010LL

#define  to(p)  e[0][p]
#define  nxt(p) e[1][p]
int e[2][maxn<<1],star[maxn],tote;
inline void AddEdge(int u,int v) {
    tote++;to(tote)=v;nxt(tote)=star[u];star[u]=tote;
}

std::priority_queue<int>heap;

int n;
int col[maxn], point[maxn], val[maxn];
int chain[maxn], fa[maxn];
bool ok;

inline void dfs(int u) {
    if (fa[u] && col[u] == col[fa[u]]) {
        chain[fa[u]] ++ ;
        if (chain[fa[u]] > 1) { ok = false ; return ; }
    }
    if (fa[u] && col[u] > col[fa[u]]) {
        ok = false ; return ;
    }
    for (int p=star[u];p;p=nxt(p))
        if (to(p)!=fa[u]) {
            fa[to(p)] = u;
            dfs(to(p)) ;
            if (!ok) return ;
        }
    if (!chain[u]) {
        point[col[u]] = u;
        val[u] = col[u];
    }
}

inline void work() {
int i, j, u, v, root1, root2;
    read(n);
    tote = 0;
    Rep (i,1,n)
        point[i] = fa[i] = chain[i] = star[i] = 0;
    Rep (i,1,n)
        read(col[i]), point[col[i]] = i ;
    rep (i,1,n) {
        read(u) ,read(v) ;
        AddEdge(u,v), AddEdge(v,u) ;
    }
    if (!point[n]) {
        puts(" Impossible") ; return ;
    }
    i = point[n] ;
    j = 0 ;
    while (true) {
        for (int p=star[i];p;p=nxt(p))
            if (to(p)!=j && col[to(p)]==n)
                {j=i,i=to(p); goto continue1;}
        break ;
        continue1:;
    }
    root1 = i; j = 0;
    while (true) {
        for (int p=star[i];p;p=nxt(p))
            if (to(p)!=j && col[to(p)]==n)
                {j=i,i=to(p);goto continue2;}
        break;
        continue2:;
    }
    root2 = i;
    if (root1 > root2) root1^=root2,root2^=root1,root1^=root2;
    ok = true ;
    dfs(root1) ;
    if (!ok) {
        puts(" Impossible") ; return ;
    }
    while (!heap.empty()) heap.pop();
    Rev (i,n,1)
        if (point[i]) {
            j = fa[point[i]];
            while (j && col[j]==i) heap.push(j), j=fa[j];
        } else {
            if (heap.empty()) {
                puts(" Impossible") ; return ;
            }
            j = heap.top(), heap.pop();
            val[j] = i;
        }
    Rep (i,1,n)
        printf(" %d",val[i]);
    puts("");
}

int main() {
int T, kiss;
//    #define READ
    #ifdef  READ
        freopen(".in" ,"r",stdin ) ;
        freopen(".out","w",stdout) ;
    #endif
    read(T) ;
    Rep (kiss,1,T) {
        printf("Case #%d:",kiss);
        work() ;
    }
    #ifdef  READ
        fclose(stdin) ; fclose(stdout) ;
    #else
        getchar() ; getchar() ;
    #endif
    return 0 ;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值