题目描述:戳这里
题解:
这题绝对有毒。。。
第一眼看到这题的题面感觉应该比较简单,只要倍增维护一下最大值和最小值就好了???
然而码了70+行之后发现题目读错了。有一个限制条件,两个点x,y是“有向”的,x到y的路径序列中,最大值的位置编号一定要大于最小值的位置编号。。。
这样就比较麻烦了[汗]。
但是也不是不可做,我们“只需要”维护五个倍增数组就可以了。
f[i][j]
f
[
i
]
[
j
]
记录i个祖先
maxx[i][j]
m
a
x
x
[
i
]
[
j
]
记录i向上推了
2j
2
j
个单位中的最大值。
minn[i][j]
m
i
n
n
[
i
]
[
j
]
记录i向上推了
2j
2
j
个单位中的最小值。
maxxu[i][j]
m
a
x
x
u
[
i
]
[
j
]
记录i向上推了
2j
2
j
个单位中的最大差值(最大值的深度小于最小值)。
maxxd[i][j]
m
a
x
x
d
[
i
]
[
j
]
记录从i向上推了
2j
2
j
个单位的那个点到它的最大差值(最大值的深度大于最小值)。
那么我们假设修正好了这四个数组,那么怎样求答案呢?
给定x,y,我们可以求出x和y的最近公共祖先g,然后就可以求一下左边的maxxu的值,和右边的maxxd的值取个max。考虑到有可能最大值在g到y的路径上,最小值在x到g的路径上,那么只要分别求一下这两个量,然后相减再和原答案取个max即可。
然后我一开始就是这么写的,然后改了半天改不出错。。。
无奈之下只好对拍,最后发现写出了点偏差。。。
我们在求出左边的maxxu值的时候因为是用倍增跳的,这样会导致只求出了某一些向
2j
2
j
个单位的答案。但是考虑到有跨过一块的情况,我们要在倍增时修正一下当前最大值或最小值,这样就能避免少算。
那么具体怎么修正这四个倍增数组,和前面倍增的过程差不多,修正一下左边和右边,然后在中间合并一下就好了。
代码如下:
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=30005,maxm=50005;
int n,m,Q,tot,b[maxn],lnk[maxn],son[2*maxn],nxt[2*maxn],fa[maxn],dep[maxn];
int f[maxn][21],maxx[maxn][21],maxxu[maxn][21],maxxd[maxn][21],minn[maxn][21];
ll sum;
struct dyt{
int x,y,w;
bool operator <(const dyt &b) const{return w>b.w;}
}a[maxm];
void add(int x,int y){son[++tot]=y,nxt[tot]=lnk[x],lnk[x]=tot;}
int getfa(int x){if (fa[x]!=x) fa[x]=getfa(fa[x]); return fa[x];}
void clear(){
tot=0; sum=0;
memset(f,0,sizeof(f));
memset(fa,0,sizeof(fa));
memset(minn,63,sizeof(minn));
memset(maxx,0,sizeof(maxx));
memset(maxxu,0,sizeof(maxxu));
memset(maxxd,0,sizeof(maxxd));
memset(lnk,0,sizeof(lnk));
memset(nxt,0,sizeof(nxt));
memset(dep,0,sizeof(dep));
}
void dfs(int x,int las){
for (int j=lnk[x];j;j=nxt[j])
if (las!=son[j]) {
f[son[j]][0]=x; dep[son[j]]=dep[x]+1;
maxx[son[j]][0]=max(b[x],b[son[j]]); minn[son[j]][0]=min(b[x],b[son[j]]);
maxxu[son[j]][0]=max(0,b[x]-b[son[j]]); maxxd[son[j]][0]=max(0,b[son[j]]-b[x]);
dfs(son[j],x);
}
}
void make_(){
for (int j=1;j<=20;j++)
for (int i=1;i<=n;i++) {
f[i][j]=f[f[i][j-1]][j-1];
minn[i][j]=min(minn[i][j-1],minn[f[i][j-1]][j-1]);
maxx[i][j]=max(maxx[i][j-1],maxx[f[i][j-1]][j-1]);
maxxu[i][j]=max(0,max(max(maxxu[i][j-1],maxxu[f[i][j-1]][j-1]),maxx[f[i][j-1]][j-1]-minn[i][j-1]));
maxxd[i][j]=max(0,max(max(maxxd[i][j-1],maxxd[f[i][j-1]][j-1]),maxx[i][j-1]-minn[f[i][j-1]][j-1]));
}
}
int get(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
for (int j=20;j>=0;j--)
if (dep[f[x][j]]>=dep[y]) x=f[x][j];
if (x==y) return y;
for (int j=20;j>=0;j--)
if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
return f[x][0];
}
int getans(int x,int y){
int ret=0;
if (dep[x]>dep[y]){
int mn=1<<30;
for (int j=20;j>=0;j--)
if (dep[f[x][j]]>=dep[y]) {
ret=max(ret,maxx[x][j]-mn); mn=min(mn,minn[x][j]);
ret=max(ret,maxxu[x][j]),x=f[x][j];
}
} else {
int mx=0;
for (int j=20;j>=0;j--)
if (dep[f[y][j]]>=dep[x]) {
ret=max(ret,mx-minn[y][j]); mx=max(mx,maxx[y][j]);
ret=max(ret,maxxd[y][j]),y=f[y][j];
}
}
return ret;
}
int getans1(int x,int y,int grand){
if (x==grand||y==grand) return 0;
int mxx=0,mnn=1<<30;
for (int j=20;j>=0;j--)
if (dep[f[x][j]]>=dep[grand]) mnn=min(mnn,minn[x][j]),x=f[x][j];
for (int j=20;j>=0;j--)
if (dep[f[y][j]]>=dep[grand]) mxx=max(mxx,maxx[y][j]),y=f[y][j];
return max(0,mxx-mnn);
}
int main(){
while (~scanf("%d",&n)) {
clear();
for (int i=1;i<=n;i++) fa[i]=i,scanf("%d",&b[i]);
scanf("%d",&m);
for (int i=1;i<=m;i++) scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].w);
sort(a+1,a+1+m);
for (int i=1;i<=m;i++) {
int fax=getfa(a[i].x),fay=getfa(a[i].y);
if (fax!=fay) {
sum+=1ll*a[i].w; fa[fax]=fay;
add(a[i].x,a[i].y); add(a[i].y,a[i].x);
}
}
printf("%lld\n",sum);
maxx[1][0]=b[1]; minn[1][0]=b[1]; dep[1]=1;
dfs(1,0); make_();
scanf("%d",&Q);
while (Q--) {
int x,y;
scanf("%d %d",&x,&y);
int grand=get(x,y);
printf("%d\n",max(0,max(max(getans(x,grand),getans(grand,y)),getans1(x,y,grand))));
}
}
return 0;
}