P9386 [THUPC 2023 决赛] 大纲 题解

· · 题解

Description

给定一棵有 n 个点的树和数列 a_1,a_2\dots a_n,若 a_i=-1 表示点 i 的值不确定,否则点 i 的值为非负整数 a_i

你需要给每一个值不确定的点赋一个非负整数,使得该树满足以下条件:

问是否存在一组解,存在输出 Reasonable,不存在输出 Unreasonable

Solution

对于一个点 i,如果其不确定,则它的初始取值下界为 0,上界为 1e9,如果确定,上界为 a_i,下界为 a_i,设 u_i 表示 i 的上界,b_i 表示 i 的下界。

遍历整棵树,对于点 x,求出儿子中最大的 b_{son_x}(也就是 maxn_x)以及数量 cnt 和最大的 u_{son_x}(设为 maxx_x,其实只有 1e9a_{son_x} 两种,也可以特判是否有不确定的点)。

由 $maxn_x$ 的定义得出,此时的 $maxn_x$ 还要再加上 $[cnt>1]$。 若 $a_x=-1$,更新上下界,满足条件,记住一定要更新上下界,第三个测试点 ```WA``` 的可能是因为这个。 若 $a_x\ne-1$,分类讨论: - $a_x=maxn_x$,满足条件。 - $a_x<maxn_x$,下界都比当前大,绝对不满足条件。 - $a_x>maxn_x\ and\ a_x\le maxx_x$,虽然已经确定的儿子的下界取不到,但是儿子中还有不确定的,将其改为 $a_x$ 就满足条件了。 - $a_x>maxn_x\ and\ a_x>maxx_x$,不满足条件,因为儿子都确定了,所以无法改。 如此 ```dfs``` 即可。 ## _Code_ ```cpp #include<bits/stdc++.h> using namespace std; #define inf 2000000000 int t; int n; int tot,head[100010]; int a[100010],b[100010],u[100010]; //a原数值,b下界,u上界 struct edge{ int to,next; }e[200010]; void add(int x,int y){ e[++tot].to=y; e[tot].next=head[x]; head[x]=tot; } void clean(){ //多测清空 tot=0; for(int i=1;i<=n;i++){ head[i]=0; } } bool dfs(int x,int fax){ int maxn=-10,cnt=0,maxx=-10; for(int i=head[x];i;i=e[i].next){ int y=e[i].to; if(y==fax) continue; if(!dfs(y,x)) return 0; //儿子都已经不满足条件了,可以直接退出 if(b[y]>maxn){ maxn=b[y]; cnt=1; }else if(b[y]==maxn){ cnt++; } maxx=max(maxx,u[y]); } if(maxn==-10){ //叶子节点 return 1; } maxn=maxn+(cnt>1); if(a[x]!=-1){ if(a[x]<maxn) return 0; else if(a[x]==maxn) return 1; else if(a[x]<=maxx) return 1; else return 0; } if(a[x]==-1){ u[x]=maxx; b[x]=maxn; return 1; } } void solve(){ clean(); cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; if(a[i]==-1) u[i]=inf,b[i]=0; else b[i]=u[i]=a[i]; } for(int i=1;i<n;i++){ int x,y; cin>>x>>y; add(x,y); add(y,x); } if(dfs(1,0)) cout<<"Reasonable"<<endl; else cout<<"Unreasonable"<<endl; } int main(){ cin>>t; while(t--){ solve(); } return 0; } ```