1. 程式人生 > >poj3728 倍增法lca 好題!

poj3728 倍增法lca 好題!

lca的好題!網上用st表和離線解的比較多,用樹上倍增也是可以做的

不知道錯在哪裡,等刷完了這個專題再回來看

/*                    
給一顆點權樹,求出一個點對(x,y)之間的max{A,B,C}
A:x到lca路徑上的最大差值
B:lca到y路徑上的最大差值
C:x到y路徑上的最大差值
需要維護的值,x結點到的祖先,x結點到祖先路徑上的最大值,最小值,x結點到路徑上的最大收益,最小收益(可以是負數)
對於每個詢問x->y:A:x到祖先的最大收益
                 B:y到祖先的最小收益的負值
                 C:x到lca的最大值減去lca到y的最小值
*/ #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; #define maxn 50005 struct Edge{ int x,next; }e[maxn<<1]; int head[maxn],tot,n,q; inline void addedge(int u,int v){ e[tot].x=v; e[tot].next
=head[u]; head[u]=tot++; } int d[maxn],f[maxn][16],a[maxn];//深度,祖先,點權 int fm[maxn][16],fn[maxn][16],sm[maxn][16],sn[maxn][16];//最大值,最小值,最大收益,最小收益 void init(){ tot=0; memset(head,-1,sizeof head); } void dfs(int x,int fa,int dep){ d[x]=dep,f[x][0]=fa; for(int i=head[x];i!=-1
;i=e[i].next) if(fa!=e[i].x) dfs(e[i].x,x,dep+1); } int query(int x,int y){ int i,xx=0,yy=0,X=a[x],Y=a[y];//x側最大收益,y側最小收益,x側最小值,y側最大值 i=15; while(d[x]!=d[y]){//拉倒同一高度 if(abs(d[x]-d[y]) >= 1<<i) if(d[y]<d[x]) xx=max(max(xx,sm[x][i]),fm[x][i]-X),X=min(X,fn[x][i]),x=f[x][i]; else yy=min(min(yy,sn[y][i]),fn[y][i]-Y),Y=max(Y,fm[y][i]),y=f[y][i]; --i; } if(x==y) return max(max(xx,-yy),Y-X); i=15; while(i>=0){ if(f[x][i] && f[y][i] && f[x][i]!=f[y][i]){ xx=max(max(xx,sm[x][i]),fm[x][i]-X),X=min(X,fn[x][i]),x=f[x][i]; yy=min(min(yy,sn[y][i]),fn[y][i]-Y),Y=max(Y,fm[y][i]),y=f[y][i]; } --i; } i=0;//這裡還要跳一次 xx=max(max(xx,sm[x][i]),fm[x][i]-X),X=min(X,fn[x][i]),x=f[x][i]; yy=min(min(yy,sn[y][i]),fn[y][i]-Y),Y=max(Y,fm[y][i]),y=f[y][i]; return max(max(xx,-yy),Y-X); } void work(){ int x,y; init(); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); addedge(x,y);addedge(y,x); } dfs(1,0,0); fm[1][0]=fn[1][0]=a[1]; sm[1][0]=-1<<30;sn[1][0]=1<<30; for(int i=2;i<=n;i++){//先打初始狀態 fm[i][0]=max(a[i],a[f[i][0]]);//和父親比較 fn[i][0]=min(a[i],a[f[i][0]]); sm[i][0]=max(0,a[f[i][0]]-a[i]);//要麼是0,要麼賺了錢 sn[i][0]=min(0,a[f[i][0]]-a[i]);//要麼是0,要麼是虧了錢 } for(int j=1;(1<<j)<n;j++)//再打剩下狀態 for(int i=1;i<=n;i++){ int tmp=f[i][j-1];//中間態 f[i][j]=f[tmp][j-1]; fm[i][j]=max(fm[i][j-1],fm[tmp][j-1]); fn[i][j]=min(fm[i][j-1],fm[tmp][j-1]); sm[i][j]=max(max(sm[i][j-1],sm[tmp][j-1]),fm[tmp][j-1]-fn[i][j-1]);//最大收益要麼是兩段間的最大收益,要麼是祖先段的最大收益減去子孫段的最小收益 sn[i][j]=min(min(sn[i][j-1],sn[tmp][j-1]),fn[tmp][j-1]-fm[i][j-1]);//最小收益相反 } scanf("%d",&q); while(q--){ scanf("%d%d",&x,&y); printf("%d\n",query(x,y)); } } int main(){ work(); return 0; }