LGP5854 [LG TPLT] 笛卡尔树 学习笔记
Luogu Link
题意简述
给定一个长为 \(n\) 的排列 \(p\),以 \(i\) 为键,\(p_i\) 为值构建 \(p\) 中所有元素的笛卡尔树。
做法解析
定义“右链”为从根开始一直往右儿子走形成的一条链。
因为我们照键从小到大的顺序插入每一个元素,所以我们在插入到元素 \((i,p_i)\) 时,其一定不是任何结点的左儿子,也就是说其一定在右链上。我们在右链从下往上找到第一个值大于 \(p_i\) 的元素 \(u\),然后让 \(i\) 成为 \(u\) 的右儿子,把 \(u\) 的原右子树,也就是“右链”被 \(u\) 截下来的部分作为 \(u\) 的左子树。
发现右链上的 \(p_i\) 有单调性,于是用单调栈维护右链的值,这样时间复杂度得到保证 \(O(N)\)。
代码实现
#include <bits/stdc++.h>
using namespace std;
namespace obasic{typedef long long lolo;template <typename _T>void readi(_T &x){_T k=1;x=0;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';x*=k;return;}template <typename _T>void writi(_T x){if(x<0)putchar('-'),x=-x;if(x>9)writi(x/10);putchar(x%10+'0');}
};
using namespace obasic;
const int MaxN=1e7+5;
int N,W[MaxN],stk[MaxN],ktp;
int ls[MaxN],rs[MaxN];lolo ans1,ans2;
int main(){readi(N);for(int i=1;i<=N;i++)readi(W[i]);for(int i=1;i<=N;i++){int k=ktp;while(k&&W[stk[k]]>W[i])k--;if(k)rs[stk[k]]=i;if(k<ktp)ls[i]=stk[k+1];stk[++k]=i,ktp=k;}for(int i=1;i<=N;i++)ans1^=1ll*i*(ls[i]+1);for(int i=1;i<=N;i++)ans2^=1ll*i*(rs[i]+1);writi(ans1),putchar(' '),writi(ans2);return 0;
}
反思总结
这道题键是下标所以直接顺着插就行了,否则要先把元素照键排个序。