题意:给定一棵树,然后多个询问,a, b,将树上a到b的最短路径上,结点的权值按顺序写下来,求出里面最长的单调递增连续子序列的长度。
先说这道题有个简化的版本,用线段树解决:LCIS
对于LCIS这题,在合并左右区间时如果左区间的右端点的值v1,小于右区间的左端点的值v2,那么可以把它们这两边连接起来,更新答案,所以线段树的结点要附加上结点左右端点的值,连接两个端点的最长序列的长度,以及本节点的最优值,然后不断合并。代码就不贴了,建议先做这个,再来搞本题。
说回本题,要处理的问题从一条线段变成一棵树,所以采用树链剖分来做。
树链剖分其实就是将树划分成轻链和重链,每条链就是一个线性的区间,然后分而治之,然后合并起来。
可以参考这篇文章:http://blog.sina.com.cn/s/blog_6974c8b20100zc61.html
自己把代码写一遍应该就会有所理解了。
首先是常规的树链剖分,对每条重链建线段树。
跟LCIS不同的是,我们建线段树的时候都是从底向上增大结点编号的,但有时候我们的路径是反过来向下走的,所以除了原先单调递增的情况,我们还要记录单调递减的信息。
于是你们就会看到下面代码里面函数带有超多参数。。。。
主要思想是,如果是起点方向向上走,那么找递增,反之找递减。最后再将递增递减两条链合并(如果可以合并的话),答案就来了。。。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
const int N = 100010;
#define pb push_back
#define CLR(o) memset(o, 0, sizeof(o))
#define lson tr[o].lch
#define rson tr[o].rch
#define lchd tr[tr[o].lch]
#define rchd tr[tr[o].rch]
#define cur tr[o]
vector<int> V[N];
struct TreeNode{
int l, r, inc, des, lv, rv, illen, irlen, dllen, drlen;
int lch, rch;
}tr[N*10];
int T, n, path_cnt, node_cnt;
int v[N];//结点权值
int father[N];//每个节点的父节点
int size[N];//每个节点的子节点数,包括自身
int belong[N];//结点所属的线段树编号
int rank[N];//每个节点在所属线段树的编号
int path_dep[N];//当前重链深度最小的结点的深度
int path_top[N];//重链深度最小的结点编号
int path_size[N];//重链长度
int tree[N];//每条重链对应的线段树根节点编号
map<int, map<int,int> > MP;//用于查找线段树上结点的权值
/*以下是线段树部分代码*/
void maintain(int o){
cur.inc = max(lchd.inc, rchd.inc);
cur.des = max(lchd.des, rchd.des);
cur.lv = lchd.lv; cur.illen = lchd.illen; cur.dllen = lchd.dllen;
cur.rv = rchd.rv; cur.irlen = rchd.irlen; cur.drlen = rchd.drlen;
int left = lchd.r - lchd.l + 1;
int right = rchd.r - rchd.l + 1;
if(lchd.rv < rchd.lv){
//左孩子的右值小于右孩子的左值
int len = lchd.irlen + rchd.illen;
cur.inc = max(cur.inc, len);
if(lchd.irlen == left){
cur.illen = len;
}
if(rchd.illen == right){
cur.irlen = len;
}
}
else if(lchd.rv > rchd.lv){
//左孩子的右值大于右孩子的左值
int len = lchd.drlen + rchd.dllen;
cur.des = max(cur.des, len);
if(lchd.drlen == left){
cur.dllen = len;
}
if(rchd.dllen == right){
cur.drlen = len;
}
}
}
void build(int path_id, int o, int ll, int rr){
cur.l = ll;
cur.r = rr;
if(ll<rr){
int m = (ll+rr)>>1;
lson = ++node_cnt;
build(path_id, lson, ll, m);
rson = ++node_cnt;
build(path_id, rson, m+1, rr);
maintain(o);
}
else{
cur.inc = cur.des = 1;
cur.lv = cur.rv = MP[path_id][ll];
cur.illen = cur.irlen = cur.dllen = cur.drlen = 1;
}
}
/*
区间合并操作,返回该区间的最优值
f为1是表示递增,0表示递减,以下其他函数相同
left,right分别是左右区间的长度
rv:左区间的右端点的值
L:左区间连接左端点的最大值
R:左区间连接右端点的最大值
后三个参数类似
*/
int merge(bool f, int left, int right, int rv, int &L, int &R, int lv1, int L1, int R1){
int len = R+L1;
int M = 0;
if(f){
if(rv < lv1){
M = max(M, len);
if(R == left) L = len;
if(L1 == right) R = len;
else R = R1;
}
else R = R1;
}
else{
if(rv > lv1){
M = max(M, len);
if(R == left) L = len;
if(L1 == right) R = len;
else R = R1;
}
else R = R1;
}
return M;
}
/*
查询函数,返回当前查询区间的最优值
lv,rv:查询的区间的左右端点的值
L, R:查询的区间的左右端点连接的最长长度
*/
int query(int o, int ll, int rr, bool f, int &lv, int &rv, int &L, int &R){
if(cur.l == ll && cur.r == rr){
lv = cur.lv; rv = cur.rv;
if(f){
L = cur.illen; R = cur.irlen;
return cur.inc;
}
else{
L = cur.dllen; R = cur.drlen;
return cur.des;
}
}
int m = (cur.l + cur.r)>>1;
if(rr<=m) return query(lson, ll, rr, f, lv, rv, L, R);
else if(ll>m) return query(rson, ll, rr, f, lv, rv, L, R);
else{
int lv1, rv1, L1, R1;
int m1 = query(lson, ll, m, f, lv, rv, L, R);
int m2 = query(rson, m+1, rr, f, lv1, rv1, L1, R1);
int M = max(m1, m2);
M = max(M, merge(f, m-ll+1, rr-m, rv, L, R, lv1, L1, R1));
rv = rv1;
return M;
}
}
/*线段树部分代码结束*/
/*dfs完成树链剖分*/
void dfs(int x, int dep){
size[x] = 1;
int key=-1;
int M = 0;
for(int i=0; i<V[x].size(); i++){
int j = V[x][i];
father[j] = x;
dfs(j, dep+1);
size[x] += size[j];
if(size[j]>M){
M = size[j];
key = i;
}
}
belong[x] = 0;
for(int i=0; i<V[x].size(); i++){
int j = V[x][i];
if(i==key){
belong[x] = belong[j];
rank[x] = rank[j]+1;
}
else{
int u = belong[j];
path_size[u] = rank[j];
path_dep[u] = dep;
path_top[u] = j;
}
}
if(!belong[x]){
belong[x] = ++path_cnt;
rank[x] = 1;
}
MP[belong[x]][rank[x]] = v[x];
}
void init(){
MP.clear();
father[1] = 0;
path_cnt = node_cnt = 0;
dfs(1, 1);
int u = belong[1];
path_dep[u] = 0;
path_size[u] = rank[1];
path_top[u] = 1;
for(int i=1; i<=path_cnt; i++){
tree[i] = ++node_cnt;
build(i, tree[i], 1, path_size[i]);
}
}
int Q(int a, int b){
int M = 0;
int x = belong[a];
int y = belong[b];
int lv1, rv1, L1, R1, llen;
int lv2, rv2, L2, R2, rlen;
bool f1=0, f2=0;
int lv, rv, L, R;
//当a和b不在同一条链时
while(x!=y){
if(path_dep[x] > path_dep[y]){
if(f1){
M = max(M, query(tree[x], rank[a], path_size[x], 1, lv, rv, L, R));
M = max(M, merge(1, llen, path_size[x]-rank[a]+1, rv1, L1, R1, lv, L, R));
rv1 = rv;
llen += path_size[x]-rank[a]+1;
}
else{
M = max(M, query(tree[x], rank[a], path_size[x], 1, lv1, rv1, L1, R1));
llen = path_size[x]-rank[a]+1;
f1 = 1;
}
a = father[path_top[x]];
x = belong[a];
}
else{
if(f2){
M = max(M, query(tree[y], rank[b], path_size[y], 0, lv, rv, L, R));
M = max(M, merge(0, rlen, path_size[y]-rank[b]+1, rv2, L2, R2, lv, L, R));
rv2 = rv;
rlen += path_size[y]-rank[b]+1;
}
else{
M = max(M, query(tree[y], rank[b], path_size[y], 0, lv2, rv2, L2, R2));
rlen += path_size[y]-rank[b]+1;
f2 = 1;
}
b = father[path_top[y]];
y = belong[b];
}
}
//当a和b在同一条链时
if(rank[a]>rank[b]){
if(f2){
M = max(M, query(tree[x], rank[b], rank[a], 0, lv, rv, L, R));
M = max(M, merge(0, rlen, rank[a]-rank[b]+1, rv2, L2, R2, lv, L, R));
rv2 = rv;
rlen += rank[a]-rank[b]+1;
}
else{
M = max(M, query(tree[x], rank[b], rank[a], 0, lv2, rv2, L2, R2));
rlen = rank[a]-rank[b]+1;
f2 = 1;
}
}
else{
if(f1){
M = max(M, query(tree[x], rank[a], rank[b], 1, lv, rv, L, R));
M = max(M, merge(1, llen, rank[b]-rank[a]+1, rv1, L1, R1, lv, L, R));
rv1 = rv;
llen += rank[b]-rank[a]+1;
}
else{
M = max(M, query(tree[x], rank[a], rank[b], 1, lv1, rv1, L1, R1));
llen = rank[b]-rank[a]+1;
f1 = 1;
}
}
//合并递增和递减两条链,注意之前都是递增和递增合并,递减和递减合并
if(f1 && f2 && rv1 < rv2){
M = max(M, R1+R2);
}
return M;
}
int main(){
scanf("%d", &T);
for(int t=1; t<=T; t++){
if(t>1) puts("");
scanf("%d", &n);
for(int i=1; i<=n; i++){
V[i].clear();
scanf("%d", v+i);
}
int a, b, q;
for(int i=2; i<=n; i++){
scanf("%d", &a);
V[a].pb(i);
}
init();
scanf("%d", &q);
printf("Case #%d:\n", t);
while(q--){
scanf("%d %d", &a, &b);
printf("%d\n", Q(a, b));
}
}
return 0;
}