超级大陈题
题意
求树上x到y的最大差值,x按顺序到y走,且大的权值在小的权值之后
如果无解就是0
感想?
话说我这个人做过的题90%都会忘。。
AC完之后发现dis里面有我的dis,还写了个AC地很辛苦QAQ
然而我完全不记得
题解
三种做法?
显而易见的“结论”
先求出点u,v的最近公共祖先f,然后求u->f->v的利润最大值maxval
对于这个maxval可能有三种情况:
1:maxval是u->f的maxval
2:maxval是f->v的maxval
3:maxval是u->f的最小w[i]减去f->v的最大w[i]
1差分+树剖 (我YY的)
上面的三种情况第三种很简单。。忽略好了。。
然后一二的话就是一条链啊!这样可能有搞头
我们就先进行差分,儿子的值减去父亲的值
那么问题就转化为求这一个链上的最大连续和了啊
这个的话看起来也不是很好做
但实际上我们可以树剖一下。。
你有两种情况—>
1.就取这条重链的最大值
2.要这条重链边界(从上往下或从下往上,看操作而定),然后可以加上下一跳链的最优答案
然后细节处理一下就好了
时间复杂度O(nlog^2n)?
但是树剖的复杂度远远没有这么大。。
所以这个做法应该是可以过的。。
由于有点复杂,我就没写了
有兴趣的人可以写一写(要是在比赛我就开打了)
TJ 网上的做法
我们维护四个变量
int up[u]表示u->f的最大maxval
int down[u]表示f->u的最大maxval
int maxx[u]表示u-f的最大w[i]
int minn[u]表示u-f的最小w[i]
然后这个是可以在并查集里面更新完成的。。
然后这里有一个小难点就是每一个询问是在他们LCA弄完以后更新的。。
一开始我没有想到这一个,于是不是非常会做。。因为要是你不在LCA弄完后更新,你的X(或Y)是得不到正确答案的,因为这个时候他的“父亲”还不是LCA
但是想通了这个就很简单了。。
我们可以在他们的LCA上打一个标记,表示要处理这个询问
CODE:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=50005;
int n,m;
struct qq{int x,y,last,id;};
qq s[N*2];int num,last[N];
qq s1[N*2];int num1,last1[N];
qq s2[N*2];int num2,last2[N];
int up[N],down[N],maxx[N],minn[N];
/*up[u]表示u->f的最大maxval
down[u]表示f->u的最大maxval
maxx[u]表示u-f的最大w[i]
minn[u]表示u-f的最小w[i] */
int uu[N],vv[N];//询问 注意:这个一定要存下来
int ans[N];
int mymax (int x,int y){return x>y?x:y;}
int mymin (int x,int y){return x<y?x:y;}
void init (int x,int y)
{
num++;
s[num].x=x;s[num].y=y;
s[num].last=last[x];
last[x]=num;
}
void init1 (int x,int y,int id)
{
num1++;
s1[num1].x=x;s1[num1].y=y;
s1[num1].last=last1[x];s1[num1].id=id;
last1[x]=num1;
}
int f[N];
bool mark[N];
int find (int x)
{
if (f[x]==x) return x;
int fa=f[x];
f[x]=find(f[x]);
up[x]=mymax(mymax(up[x],up[fa]),maxx[fa]-minn[x]);
down[x]=mymax(mymax(down[x],down[fa]),maxx[x]-minn[fa]);
maxx[x]=mymax(maxx[x],maxx[fa]);
minn[x]=mymin(minn[x],minn[fa]);
return f[x];
}
void init2 (int x,int id)
{
num2++;
s2[num2].x=x;s2[num2].id=id;
s2[num2].last=last2[x];
last2[x]=num2;
}
void solve (int x)
{
mark[x]=true;f[x]=x;
for (int u=last1[x];u!=-1;u=s1[u].last)
{
int y=s1[u].y;
if (mark[y]==false) continue;
int f=find(y);
init2(f,s1[u].id);
}
for (int u=last[x];u!=-1;u=s[u].last)
{
int y=s[u].y;
if (mark[y]==true) continue;
solve(y);
f[y]=x;
}
for (int u=last2[x];u!=-1;u=s2[u].last)
{
int id=s2[u].id;
find(uu[id]);find(vv[id]);
ans[id]=mymax(mymax(up[uu[id]],down[vv[id]]),maxx[vv[id]]-minn[uu[id]]);
}
}
int main()
{
while (scanf("%d",&n)!=EOF)
{
memset(mark,false,sizeof(mark));
num=0; memset(last,-1,sizeof(last));
num1=0;memset(last1,-1,sizeof(last1));
num2=0;memset(last2,-1,sizeof(last2));
for (int u=1;u<=n;u++)
{
int w;
scanf("%d",&w);
up[u]=down[u]=0;
maxx[u]=minn[u]=w;
}
for (int u=1;u<n;u++)
{
int x,y;
scanf("%d%d",&x,&y);
init(x,y);init(y,x);
}
scanf("%d",&m);
for (int u=1;u<=m;u++)
{
scanf("%d%d",&uu[u],&vv[u]);
init1(uu[u],vv[u],u);
init1(vv[u],uu[u],u);
}
solve(1);
for (int u=1;u<=m;u++) printf("%d\n",ans[u]);
}
return 0;
}
倍增的做法
我也没写
有兴趣的人自己去看吧FYCJCP