1. 程式人生 > >POJ 4718 /// 樹鏈剖分+線段樹區間合併 求樹上兩點間的LCIS長度

POJ 4718 /// 樹鏈剖分+線段樹區間合併 求樹上兩點間的LCIS長度

題目大意:

給定n個點 每個點都有權值

接下來給定樹的n條邊 第 i 個數 a[i] 表示 i+1到a[i]之間 有一條邊

給定q q個詢問 每次詢問給出 x y 求x到y的最長上升子序列的長度

 

題解 https://blog.csdn.net/forever_wjs/article/details/52088861 

明確幾個變數的定義之後 更新部分一看就懂 就不註釋了

需要特別提到的是

我們每次合併區間是合併 新的要合併的區間(左子區間) 和 底部已合併好的區間(右子區間)

而我們在查詢過程中兩個點由底部不斷向LCA逼近

這樣當x和y逼近到LCA時 x和y對應區間的方向是 由LCA到x 由LCA到y 這樣的兩個區間

所以這兩個區間不能進行合併 因為起點相同

所以 就應該利用

x對應區間的 由右端點始下降的LCIS 和 右端點的值

y對應區間的 有左端點始上升的LCIS 和 左端點的值

再更新一次答案

#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f
#define mem(i,j) memset(i,j,sizeof(i))
#define lson l,m,rt<<1
#define
rson m+1,r,rt<<1|1 #define root 1,n,1 const int maxn=1e5+5; int n, q, w[maxn]; struct IntervalTree { struct EDGE { int to,ne; }e[maxn<<1]; int head[maxn], tot; void addE(int u,int v) { e[tot].to=v; e[tot].ne=head[u]; head[u]=tot++; } int fa[maxn], son[maxn], dep[maxn], num[maxn];
int top[maxn], p[maxn], fp[maxn], pos; void init() { tot=1; mem(head,0); pos=0; mem(son,0); } struct TREE { int l,r; // 區間左右位置 int Lw,Rw; // 左端點的值 右端點的值 int LL,LR; // 以左端點始的下降LCIS長度 以左端點始的上升LCIS長度 int RL,RR; // 以右端點始的下降LCIS長度 以右端點始的上升LCIS長度 int Len,Ren; // 該區間的 從左上升LCIS長度 從右上升LCIS長度 TREE(){ l=r=Lw=Rw=LL=LR=RL=RR=Len=Ren=0; } }tree[maxn<<2]; // --------------------以下是線段樹------------------------- TREE Merge(TREE L,TREE R) { if(R.Len==0) return L; TREE ans=L; ans.r=R.r, ans.Rw=R.Rw; ans.Len=max(L.Len,R.Len); ans.Ren=max(L.Ren,R.Ren); if(L.LL==L.r-L.l+1 && L.Rw>R.Lw) ans.LL=L.LL+R.LL; else ans.LL=L.LL; if(L.LR==L.r-L.l+1 && L.Rw<R.Lw) ans.LR=L.LR+R.LR; else ans.LR=L.LR; if(R.RL==R.r-R.l+1 && L.Rw<R.Lw) ans.RL=R.RL+L.RL; else ans.RL=R.RL; if(R.RR==R.r-R.l+1 && L.Rw>R.Lw) ans.RR=R.RR+L.RR; else ans.RR=R.RR; ans.Len=max(ans.Len,ans.LR); ans.Len=max(ans.Len,ans.RL); ans.Ren=max(ans.Ren,ans.LL); ans.Ren=max(ans.Ren,ans.RR); if(L.Rw<R.Lw) ans.Len=max(ans.Len,L.RL+R.LR); if(L.Rw>R.Lw) ans.Ren=max(ans.Ren,L.RR+R.LL); return ans; } void build(int l,int r,int rt) { tree[rt].l=l, tree[rt].r=r; if(l==r) { tree[rt].LL=tree[rt].LR=1, tree[rt].RL=tree[rt].RR=1, tree[rt].Len=tree[rt].Ren=1; tree[rt].Lw=tree[rt].Rw=fp[l]; return; } int m=(l+r)>>1; build(lson), build(rson); tree[rt]=Merge(tree[rt<<1],tree[rt<<1|1]); } TREE query(int L,int R,int l,int r,int rt) { if(L==l && r==R) return tree[rt]; int m=(l+r)>>1; if(R<=m) return query(L,R,lson); else if(L>m) return query(L,R,rson); else return Merge(query(L,m,lson),query(m+1,R,rson)); } // --------------------以上是線段樹------------------------- // --------------------以下是樹鏈剖分------------------------- void dfs1(int u,int pre,int d) { dep[u]=d; fa[u]=pre; num[u]=1; for(int i=head[u];i;i=e[i].ne) { int v=e[i].to; if(v!=fa[u]) { dfs1(v,u,d+1); num[u]+=num[v]; if(!son[u] || num[v]>num[son[u]]) son[u]=v; } } } void dfs2(int u,int sp) { top[u]=sp; p[u]=++pos; fp[p[u]]=w[u]; if(!son[u]) return; dfs2(son[u],sp); for(int i=head[u];i;i=e[i].ne) { int v=e[i].to; if(v!=son[u] && v!=fa[u]) dfs2(v,v); } } int solve(int x,int y) { int fx=top[x], fy=top[y]; TREE ans1, ans2; bool flag=0; while(fx!=fy) { if(dep[fx]>dep[fy]) { ans1=Merge(query(p[fx],p[x],root),ans1); x=fa[fx]; } else { ans2=Merge(query(p[fy],p[y],root),ans2); y=fa[fy]; } fx=top[x], fy=top[y]; } if(p[x]>p[y]) ans1=Merge(query(p[y],p[x],root),ans1); else ans2=Merge(query(p[x],p[y],root),ans2); int ans=max(ans1.Ren,ans2.Len); if(ans1.Lw<ans2.Lw) ans=max(ans,ans1.LL+ans2.LR); return ans; } // --------------------以上是樹鏈剖分------------------------- void initQTree() { dfs1(1,0,0), dfs2(1,1); build(root); } }T; int main() { int t, tcase=1; scanf("%d",&t); bool st=0; while(t--) { if(st) puts(""); scanf("%d",&n); T.init(); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=2;i<=n;i++) { int v; scanf("%d",&v); T.addE(i,v); T.addE(v,i); } T.initQTree(); scanf("%d",&q); printf("Case #%d:\n",tcase++); while(q--) { int u,v; scanf("%d%d",&u,&v); printf("%d\n",T.solve(u,v)); } st=1; } return 0; }
View Code