题目
思路
由于 n ≤ 6000 n\le 6000 n≤6000 并且 C F \tt CF CF 是少爷机,朴素的 O ( n 2 log n ) \mathcal O(n^2\log n) O(n2logn) 便可通过。
试着去优化它。可以想到,对于每个点,求出这样的一个 L I S \tt LIS LIS 是轻而易举的:一个端点是它,另一个端点在子树内。直接将序列上的做法推广到树上,加一个 d s u o n t r e e \tt dsu\; on\; tree dsuontree 就可以做到 O ( n log n ) \mathcal O(n\log n) O(nlogn) 。
接下来要做的是统计答案。显然我们可以枚举 l c a \tt lca lca ,在 d s u o n t r e e \tt dsu\; on\; tree dsuontree 的时候顺便统计——一个子树内枚举,在已有的数组中二分查找。复杂度是 O ( n log 2 n ) \mathcal O(n\log^2n) O(nlog2n) 的。
代码
朴素版:
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
template < class T >
void getMax(T&a,const T&b){if(a<b) a=b;}
template < class T >
void getMin(T&a,const T&b){if(b<a) a=b;}
const int MaxN = 6005;
struct Edge{
int to, nxt;
Edge(int T=0,int N=0){
to = T, nxt = N;
}
} edge[MaxN<<1];
int head[MaxN], cntEdge;
void addEdge(int a,int b){
edge[cntEdge] = Edge(b,head[a]);
head[a] = cntEdge ++;
edge[cntEdge] = Edge(a,head[b]);
head[b] = cntEdge ++;
}
int a[MaxN], g[MaxN], ans, n;
void dfs(int x,int pre){
int r = lower_bound(g+1,g+n+1,a[x])-g;
int temp = g[r]; g[r] = a[x];
for(int i=head[x]; ~i; i=edge[i].nxt)
if(edge[i].to != pre)
dfs(edge[i].to,x);
g[r] = temp, getMax(ans,r);
}
int main(){
n = readint();
for(int i=1; i<=n; ++i){
head[i] = -1;
g[i] = MaxN*MaxN;
a[i] = readint();
}
for(int i=1; i<n; ++i)
addEdge(readint(),readint());
for(int i=1; i<=n; ++i) dfs(i,-1);
printf("%d\n",ans);
return 0;
}
优化版(我没有特别去注意常数):
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
template < class T >
void getMax(T&a,const T&b){if(a<b) a=b;}
template < class T >
void getMin(T&a,const T&b){if(b<a) a=b;}
const int MaxN = 6005;
struct Edge{
int to, nxt;
Edge(int T=0,int N=0){
to = T, nxt = N;
}
} edge[MaxN<<1];
int head[MaxN], cntEdge;
void addEdge(int a,int b){
edge[cntEdge] = Edge(b,head[a]);
head[a] = cntEdge ++;
edge[cntEdge] = Edge(a,head[b]);
head[b] = cntEdge ++;
}
template < class T , int normal >
class L_S{ // LIS or LDS
int g[MaxN], n; T cmp;
public:
void clear(){ n = 0; }
int query(int x){
int L = 0, R = n;
while(L < R)
if(cmp(g[(L+R+1)/2],x))
L = (L+R+1)>>1;
else R = (L+R+1)/2-1;
return L+1;
}
void modify(int id,int x){
while(n < id)
g[++ n] = normal;
if(cmp(x,g[id]))
g[id] = x; // 请自行保证有序
}
};
int son[MaxN], siz[MaxN];
void build(int x,int pre){
siz[x] = 1, son[x] = 0;
for(int i=head[x]; ~i; i=edge[i].nxt)
if(edge[i].to != pre){
build(edge[i].to,x);
if(siz[edge[i].to] > siz[son[x]])
son[x] = edge[i].to;
siz[x] += siz[edge[i].to];
}
}
const int infty = (1<<30)-1;
int a[MaxN], n, ans, up[MaxN], down[MaxN];
L_S< less<int>,infty > LIS;
L_S< greater<int>,-infty > LDS;
void modify(int x,int pre){
LIS.modify(up[x],a[x]);
LDS.modify(down[x],a[x]);
for(int i=head[x]; ~i; i=edge[i].nxt)
if(edge[i].to != pre)
modify(edge[i].to,x);
}
void updata(int rt,int x,int pre){
for(int i=head[x]; ~i; i=edge[i].nxt)
if(edge[i].to != pre)
updata(rt,edge[i].to,x);
getMax(ans,LIS.query(a[x])+down[x]-1);
if(a[x] > a[rt])
getMax(ans,LIS.query(a[rt])+down[x]);
getMax(ans,LDS.query(a[x])+up[x]-1);
if(a[x] < a[rt])
getMax(ans,LDS.query(a[rt])+up[x]);
}
void dfs(int x,int pre,bool f){
for(int i=head[x]; ~i; i=edge[i].nxt)
if(edge[i].to != pre)
if(edge[i].to != son[x])
dfs(edge[i].to,x,0);
if(son[x]) dfs(son[x],x,1);
for(int i=head[x]; ~i; i=edge[i].nxt)
if(edge[i].to != pre)
if(edge[i].to != son[x]){
updata(x,edge[i].to,x);
modify(edge[i].to,x);
}
up[x] = LIS.query(a[x]);
down[x] = LDS.query(a[x]);
if(!f) LIS.clear(), LDS.clear();
else{
LIS.modify(up[x],a[x]);
LDS.modify(down[x],a[x]);
}
}
int main(){
n = readint();
for(int i=1; i<=n; ++i){
head[i] = -1;
a[i] = readint();
}
for(int i=1; i<n; ++i)
addEdge(readint(),readint());
dfs(1,-1,1);
for(int i=1; i<=n; ++i)
getMax(ans,max(up[i],down[i]));
printf("%d\n",ans);
return 0;
}