CF2171F Rae Taylor and Trees (hard version) 题解
Mr_RedStone · · 题解
思路
Easy ver.
先考虑简单版,刚开始盲猜只要有
但是这是错误的,例如样例中的:
7
4 3 5 7 6 2 1
其中有
更加极端的有如下样例:
6
6 1 2 3 4 5
显然
由于边是无向的,因此只要能使
3
2 1 3
显然
最后判断是否只剩一个连通块即可。
Hard ver.
简单版做出来后,困难版就秒了。
其实刚才的过程中已经包含建树了:每次入栈元素与栈顶连通块中最大值合并。这样一次连通块合并只产生一条边,若可行则产生
代码
AC 记录
#include<bits/stdc++.h>
using namespace std;
const int NR=2e5+5;
int T,n;
int a[NR];
int f[NR],sz[NR];
vector<int> g[NR];
int find(int x){
if(f[x]==x) return x;
return f[x]=find(f[x]);
}
bool merge(int x,int y){
int t1=find(x),t2=find(y);
if(t1==t2) return 0;
if(sz[t1]<sz[t2]){
f[t1]=t2;
sz[t2]+=sz[t1];
}
else{
f[t2]=t1;
sz[t1]+=sz[t2];
}
return 1;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
f[i]=i,sz[i]=1;
g[i].clear();
}
stack<pair<int,int>> st;//first表示实际下标,second表示first所在连通块的最大值的下标
int cnt=n;
for(int i=n;i>=1;i--){
int mx=i;
while(!st.empty()&&a[i]<a[st.top().second]){//与最大值比较
cnt-=merge(i,st.top().first);//与实际下标合并
if(a[st.top().second]>a[mx]) mx=st.top().second;
g[i].push_back(st.top().second);//与最大值连树边
st.pop();
}
st.push(make_pair(i,mx));
}
if(cnt!=1) printf("No\n");
else{
printf("Yes\n");
for(int i=1;i<=n;i++){
for(auto j:g[i]){
printf("%d %d\n",a[i],a[j]);
}
}
}
}
return 0;
}
/*
1
6
1 3 4 5 2 6
*/
简单版和困难版真的有区别吗?