题目大意
给你一棵树,每个点都有一个权值,让你在所有点中选择一个点删除,使得剩下的子树的最长上升子序列长度最小
解题思路
首先如何求树上最长上升子序列
如何删点
首先,我们任取一个点
p
p
p 考虑删除点
p
p
p 的结果,删除点
p
p
p 之后,我们只需要考虑
p
p
p 的子树的最长上升子序列长度,即为答案,同时我们记录一下是哪一棵子树的最长上升子序列最长,我们如果要删其他点的话,那要删去的点一定在这棵子树之中(因为你再删除了这棵子树上的点的其他点是没有意义的,并不会对答案造成贡献)。
然后,如何选取
p
p
p 显然,我们每次选取要删点的树的重心即可,不然出现一条单链的话复杂度不够。
这样我们的复杂度是
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 1e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int head[MAXN];
int val[MAXN];
int n;
int ans;
struct edge{
int next, to, w;
}e[MAXN << 1];
int cnt;
void init(){
for(int i = 0; i <= n; i++)
head[i] = -1;
cnt = 0;
}
void add(int u, int v, int w = 0){
e[cnt].w = w;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt++;
};
int tot;
struct Tree{
int l, r;
int lis, lds;
}tree[MAXN * 600];
int rt[MAXN];
int sz[MAXN];
int root, maxPart;
int res;
int vis[MAXN];
void getrt(int S, int u, int f){
sz[u] = 1;
int maxx = 0;
for(int i = head[u]; ~i; i = e[i].next){
int v = e[i].to;
if(vis[v] || f == v)
continue;
getrt(S, v, u);
sz[u] += sz[v];
maxx = max(maxx, sz[v]);
}
maxx = max(maxx, S - sz[u]);
if(maxx < maxPart){
maxPart = maxx;
root = u;
}
}
int build(){
tot++;
tree[tot].l = tree[tot].r = tree[tot].lis = tree[tot].lds = 0;
return tot;
}
void update(int &p, int l, int r, int pos, int vlis, int vlds){
if(p == 0)
p = build();
tree[p].lis = max(tree[p].lis, vlis);
tree[p].lds = max(tree[p].lds, vlds);
if(l == r)
return ;
int m = (l + r) >> 1;
if(pos <= m)
update(tree[p].l, l, m, pos, vlis, vlds);
else
update(tree[p].r, m+1, r, pos, vlis, vlds);
}
int merge(int p, int q){
if(!p || !q)
return p + q;
tree[p].lis = max(tree[p].lis, tree[q].lis);
tree[p].lds = max(tree[p].lds, tree[q].lds);
// 最大上升子序列长度
res = max(res, max(tree[tree[p].l].lis + tree[tree[q].r].lds,
tree[tree[p].r].lds + tree[tree[q].l].lis));
tree[p].l = merge(tree[p].l, tree[q].l);
tree[p].r = merge(tree[p].r, tree[q].r);
return p;
}
PII query(int p, int l, int r, int L, int R){ // L R 询问
if(!p || l > r)
return {0, 0};
if(L <= l && r <= R)
return {tree[p].lis, tree[p].lds};
int m = (l + r) >> 1;
PII ret = {-inf, -inf};
PII tr, tl;
if(L <= m)
tl = query(tree[p].l, l, m, L, R);
if(m < R)
tr = query(tree[p].r, m+1, r, L, R);
ret.fi = max(tl.fi, tr.fi);
ret.se = max(tl.se, tr.se);
return ret;
}
void dfs(int u, int f){
rt[u] = 0;
for(int i = head[u]; ~i; i = e[i].next){
int v = e[i].to;
if(v == f)
continue;
dfs(v, u);
}
int nlis = 0, nlds = 0;
for(int i = head[u]; ~i; i = e[i].next){
int v = e[i].to;
if(v == f)
continue;
int lis = query(rt[v], 1, n, 1, val[u]-1).fi;
int lds = query(rt[v], 1, n, val[u]+1, n).se;
rt[u] = merge(rt[u], rt[v]);
res = max(res, lis + 1 + nlds);
res = max(res, nlis + 1 + lds);
nlis = max(nlis, lis);
nlds = max(nlds, lds);
}
update(rt[u], 1, n, val[u], nlis + 1, nlds + 1);
}
void calc(int S, int u){
maxPart = S;
getrt(S, u, 0);
vis[root] = 1;
int maxx = 0, pos = -1;
for(int i = head[root]; ~i; i = e[i].next){
int v = e[i].to;
res = 0;
dfs(v, root);
if(res > maxx){
maxx = res;
pos = v;
}
}
ans = min(ans, maxx);
if(pos == -1 || vis[pos])
return ;
calc(sz[pos], pos);
}
void solve(){
cin >> n;
init();
for(int i = 1; i < n; i++){
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
for(int i = 1; i <= n; i++)
cin >> val[i];
ans = inf;
calc(n, 1);
cout << ans << endl;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
qc;
int T;
// cin >> T;
T = 1;
while(T--){
solve();
}
return 0;
}