THUSC 2021 D1T2 及 CF490F 题解
前言:
发现题解区没有点分治(哭),树上路径问题不应该直接无脑大力点分治吗(哭哭)
这里提供一种
题意
给出一颗带点权树,求树上所有路径的严格上升子序列的最大值。
题解
正如前言所述,我们思考点分治在当前分治中心
一个显然的思路是记录
他们的求法可以通过离散化+记录时间戳
然后将
- 子序列包含
u :up[w-1]+dn[w+1]+1 - 子序列不包含
u :\max {up[i]+dn[i+1]}
但聪明的你肯定会想到,要是两条路径分别来自同一棵子树,那不就寄了吗 QAQ
所以套路的,对
常数在校内 OJ
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read() {
int x=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) x=x*10+ch-48, ch=getchar();
return x;
}
const int N=1e5+3;
int n, ans, a[N], b[N], m, c[N];
vector<int> e[N];
int rt, all, mx[N], sz[N], vis[N];
int L[N], R[N], tim, bl[N];
int up1[N], gu1[N], up2[N], gu2[N], dn1[N], gd1[N], dn2[N], gd2[N];
vector<int> id[N];
void get_root(int u, int dad) {
mx[u]=0, sz[u]=1;
for (int v:e[u]) if (v!=dad&&!vis[v]) {
get_root(v, u);
mx[u]=max(mx[u], sz[v]);
sz[u]+=sz[v];
}
mx[u]=max(mx[u], all-sz[u]);
if (mx[u]<mx[rt]) rt=u;
}
void dfs(int u, int dad) {
sz[u]=1;
b[++m]=a[u];
L[u]=++tim;
for (int v:e[u]) if (v!=dad&&!vis[v]) {
dfs(v, u);
sz[u]+=sz[v];
}
R[u]=tim;
}
void dfs(int u, int dad, int o) {
c[u]=lower_bound(b+1, b+1+m, a[u])-b;
bl[u]=o;
for (int v:e[u]) if (v!=dad&&!vis[v]) {
dfs(v, u, o);
}
id[c[u]].push_back(u);
}
#define Rt 1, 1, all
#define ls (i<<1)
#define rs (i<<1|1)
#define mid ((l+r)>>1)
#define Ls ls, l, mid
#define Rs rs, mid+1, r
int t[N<<2], lz[N<<2];
int ask(int i, int l, int r, int x, int y) {
if ((x<=l&&r<=y)||!t[i]) return t[i];
if (lz[i]) t[ls]=t[rs]=0, lz[ls]=lz[rs]=1, lz[i]=0;
if (x>mid) return ask(Rs, x, y);
if (y<=mid) return ask(Ls, x, y);
return max(ask(Ls, x, y), ask(Rs, x, y));
}
void add(int i, int l, int r, int x, int v) {
if (l==r) return t[i]=v, void();
if (lz[i]) t[ls]=t[rs]=0, lz[ls]=lz[rs]=1, lz[i]=0;
if (x<=mid) add(Ls, x, v);
else add(Rs, x, v);
t[i]=max(t[ls], t[rs]);
}
void New(int &fir, int &ff, int &sec, int &ss, int tmp, int o) {
if (tmp>fir) {
if (ff!=o||ss==o) sec=fir, ss=ff;
fir=tmp, ff=o;
}
else if (tmp>sec&&ff!=o) {
sec=tmp, ss=o;
}
}
void calc(int i, int &fir, int &ff, int &sec, int &ss) {
fir=ff=sec=ss=0;
queue<int> q;
for (int u:id[i]) {
int tmp=ask(Rt, L[u], R[u])+1;
New(fir, ff, sec, ss, tmp, bl[u]);
q.push(tmp);
}
for (int u:id[i]) add(Rt, L[u], q.front()), q.pop();
}
void split(int u) {
m=tim=0;
dfs(u, 0);
sort(b+1, b+1+m);
m=unique(b+1, b+1+m)-b-1;
for (int v:e[u])
if (!vis[v]) dfs(v, u, v);
c[u]=lower_bound(b+1, b+1+m, a[u])-b;
lz[1]=1, t[1]=0;
for (int i=1; i<=m; i++) {
calc(i, up1[i], gu1[i], up2[i], gu2[i]);
New(up1[i], gu1[i], up2[i], gu2[i], up1[i-1], gu1[i-1]);
New(up1[i], gu1[i], up2[i], gu2[i], up2[i-1], gu2[i-1]);
}
dn1[m+1]=gd1[m+1]=dn2[m+1]=gd2[m+1]=0;
lz[1]=1, t[1]=0;
for (int i=m; i>=1; i--) {
calc(i, dn1[i], gd1[i], dn2[i], gd1[i]);
New(dn1[i], gd1[i], dn2[i], gd2[i], dn1[i+1], gd1[i+1]);
New(dn1[i], gd1[i], dn2[i], gd2[i], dn2[i+1], gd2[i+1]);
}
if (gu1[c[u]-1]!=gd1[c[u]+1]) ans=max(ans, up1[c[u]-1]+dn1[c[u]+1]+1);
else {
ans=max({ans, up1[c[u]-1]+dn2[c[u]+1]+1, up2[c[u]-1]+dn1[c[u]+1]+1});
}
for (int i=1; i<m; i++) {
if (gu1[i]!=gd1[i+1]) ans=max(ans, up1[i]+dn1[i+1]);
else {
ans=max({ans, up1[i]+dn2[i+1], up2[i]+dn1[i+1]});
}
}
for (int i=1; i<=m; i++) id[i].clear();
vis[u]=1;
for (int v:e[u]) if (!vis[v]) {
rt=0, all=sz[v], get_root(v, u);
split(rt);
}
}
signed main() {
n=read();
for (int i=1; i<=n; i++) a[i]=read();
for (int i=1; i<n; i++) {
int u=read(), v=read();
e[u].push_back(v), e[v].push_back(u);
}
mx[0]=n+1;
rt=0, all=n, get_root(1, 0);
split(rt);
cout<<ans<<'\n';
return 0;
}