题目
给一棵n(n<=5e5)个点的树,问是否存在节点x,使得以x为根时,树恰为二叉搜索树
如果不存在输出-1,否则增序输出所有可能的节点x
思路来源
乱搞AC
题解
大概能树形dp搞吧,但是不会
于是变成了一道一堆特判冲过去的题
首先特判一些情况,
①存在度数>=4的点的时候无解,②n<=2必有解(此时根的度为1)
注意到,③答案可能的节点,一定是最小值和最大值之间的这条链上的节点,
不然必有一个值的大小关系和最值是矛盾的,
所以枚举这条链上的点u,u最多相邻三个点,链上左点lu,链上右点ru,和子节点v,
④如果有子节点v,则v必须是一棵二叉搜索子树:
即如果v<u,则mx[v]必须小于u,反之同理;
记v<u的子树为左子树,v>u的子树为右子树,则u最多只有一棵左子树和一棵右子树(废话)
⑤此外还有一个重要特判,最小值和最大值最多只能连接两个点,
即最小值只能有右子树和父亲两个比它大的值,
考虑n=4,m=3,1 2, 1 3, 1 4三条边的一棵树,是无解的
⑥这条链上的点,从最小值到最大值之间的值,如果不单调递增,也是无解的
⑦对于u来说,如果有v<u的子树,即左子树v,则链上左点lu在这棵树中一定比链上右点高,
说明根节点只能是lu及lu以左的点,反之同理,每一棵v子树都对根节点有一个区间限制
若最终区间为空,无解;否则,区间内每一个度不超过2的点都是一个合法的解
代码
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=5e5+10;
vector<int>e[N],f,ans;
int n,u,v,par[N],mn[N],mx[N],Mn,Mx,c;
bool sol=1,link[N],no[N];
void dfs(int u,int fa){
par[u]=fa;
int small=0,big=0;
for(auto &v:e[u]){
if(v==fa)continue;
dfs(v,u);
if(v<u){
if(mx[v]>u)sol=0;
small++;
}
else{
if(mn[v]<u)sol=0;
big++;
}
mn[u]=min(mn[u],mn[v]);
mx[u]=max(mx[u],mx[v]);
}
if(small>=2 || big>=2){
no[u]=1;
}
if((u==1 || u==n) && e[u].size()==3){
sol=0;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
mn[i]=mx[i]=i;
}
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
if(u==v){
puts("-1");
return 0;
}
e[u].pb(v);e[v].pb(u);
if(e[u].size()>=4 || e[v].size()>=4){//度=4
puts("-1");
return 0;
}
}
if(n<=2){//n<=2
for(int i=1;i<=n;++i){
printf("%d%c",i," \n"[i==n]);
}
return 0;
}
dfs(n,n+1);
if(!sol){//非二叉搜索子树
puts("-1");
return 0;
}
for(int u=1;;u=par[u]){//不单调
if(u>par[u]){
puts("-1");
return 0;
}
link[u]=1;
f.push_back(u);
if(u==n)break;
}
for(auto &u:f){
for(auto &v:e[u]){
if(link[v])continue;
if(no[v]){
puts("-1");
return 0;
}
}
}
Mx=0;Mn=f.size()-1;
for(int i=0;i<f.size();++i){
int u=f[i];
for(auto &v:e[u]){
if(link[v])continue;
if(v<u){
if(mn[v]<f[i-1]){
puts("-1");
return 0;
}
Mn=min(Mn,i-1);
}
else if(v>u){
if(mx[v]>f[i+1]){
puts("-1");
return 0;
}
Mx=max(Mx,i+1);
}
}
}
for(int i=Mx;i<=Mn;++i){
int u=f[i];
if(e[u].size()>2)continue;
ans.pb(u);
}
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();++i){
printf("%d%c",ans[i]," \n"[i==(int)ans.size()-1]);
}
return 0;
}