pojThe merchant

超级大陈题

题意

求树上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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值