比赛rank15,第一次这么靠前诶
题意
一棵节点数为
n
的树,树节点编号
给出这棵新树的描述,问是否可以还原为原树?输出原图节点
1…n
的权值,若有多种答案输出字典序最小的答案,无答案输出
Impossible
题解
考虑原图变为新图后新图的性质。
- 权值 n 一定出现在新图中
- 新图中每种权值必然连成一条链
- 新图中从根开始向下遍历遇到的权值一定是非严格递减的
于是这三条性质成为我们判断
读入数据后先判定是否存在权值
n
。根据性质
找到权值
n
所在的链,新树的根必然是这条链的两个端点之一。可以发现根的选择不影响其它链的上下方向及链之间的相对关系。而根的另一头一定填
下面从根开始 dfs ,遍历时记录一下 father 。
遍历到一个节点 u 时:
如果
如果
u
的权值大于
遍历 u 的儿子后返回
如果
跳出
从
n
开始到
如果当前权值已经被确定位置,即为某一条权值链的最下端,则取出这条权值链中其它节点的位置,存到一个大根堆中。注意这些位置对之后枚举的权值都是可行的位置,因为是从大到小枚举的权值。
如果当前权值未被确定位置,则从大根堆中取出堆顶,将此权值放入取出的位置上。这个地方是贪心使得字典序最小,若当前权值不使用这个最大的位置,通过交换当前权值和这个位置上的权值可以得到更优解。
如果当前权值未被确定位置,而堆空了,则
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 ;
}