poj3728 倍增法lca 好题!
lca的好题!网上用st表和离线解的比较多,用树上倍增也是可以做的
不知道错在哪里,等刷完了这个专题再回来看
题解链接https://blog.csdn.net/Sd_Invol/article/details/9572423
/* 给一颗点权树,求出一个点对(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; }