有一个有根树,带点权,最多500个叶节点,满足根到叶子单条链上的点权严格递减 同时告诉了每对叶节点(i,j)的lca的点权,构建一棵符合条件的树。
首先这个题肯定是从叶子节点往上去构建一棵树的过程,
想到可以用并查集来维护,fa[u]就表示u往上走目前到达的点,
我们把(i,j,lca(i,j))
三元组按点权升序排序,点权相同则按顶点编号排序。
枚举排好序的三元组,对于(u,v,w)
,有两种情况。
- 先求出u v的根节点r1 r2 ,如果小于w 说明u v的lca的点权就是w,n+=1表示新建一个节点,也就是他们lca的节点编号是n+1.
- 也可能是r1\r2等于w 那么说明r1\r2就是lca
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 2e5 + 10;
const ll mod = 1e9 + 7;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){while(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){while(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int fa[maxn];
struct node
{
int u,v,w;
bool operator <(const node &f)const
{
if(w != f.w)
return w<f.w;
if(u != f.u)
return u<f.u;
return v<f.v;
}
}b[maxn];
int cnt;
int a[maxn];//点权
int n;//1~n叶子节点
vector<pii> ans;
int find(int x)
{
if(fa[x] == x) return x;
fa[x]=find(fa[x]);
return fa[x];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
int d;scanf("%d",&d);
if(i<j) b[++cnt]=(node){i,j,d};
if(i==j) a[i]=d;
}
fa[i]=i;
}
sort(b+1,b+cnt+1);
for(int i=1;i<=cnt;i++)//开始构建这棵树
{
int r1=find(b[i].u),r2=find(b[i].v);
if(r1==r2) continue;
if(max(a[r1],a[r2])==b[i].w)
{
if(a[r1]==b[i].w)
{
fa[r2]=r1;
ans.push_back({r2,r1});
}
else
{
fa[r1]=r2;
ans.push_back({r1,r2});
}
}
else //小于
{
fa[++n]=n;
a[n]=b[i].w;
fa[r1]=n,fa[r2]=n;
ans.push_back({r1,n});
ans.push_back({r2,n});
}
}
cout<<n<<'\n';
for(int i=1;i<=n;i++) printf("%d ",a[i]);
puts("");
printf("%d\n",find(1));
for(pii e:ans) printf("%d %d\n",e.first,e.second);//first是孩子节点 second是父节点
return 0;
}