POJ3728-LCA及诸多优化

53 篇文章 1 订阅

这题是一个在一开始就能吓住人,然后分析分析,再分析分析,再分析分析,发现能做的好题。

题意:给出一个结点带权price的树状图(结点数<=50000),询问(询问次数<=50000)树中从u到v中能赚到的最多的差价是多少。

首先最朴素的想法是,找出u到v的路径(用到LCA),完整地访问一遍得结果。

朴素的想法是出发点,虽不可行,觉得有可行的优化空间。

优化线索:询问之间有路径的重复,重复的部分可通过一些记录来除掉重复访问和计算;

对于这条线索,我的想法是可以先处理路径较短的询问,再处理路径较长的询问,这样能最好的记录有效的实时信息。

很抽象?先随便确定一个根把树变成有根树之后,对树按层次遍历来编号num[i],那么对于所有的询问(u,v),按其LCA(u,v)的num从大到小排序,于是就可以按路径的长短顺序来处理各个询问了。然后一边处理询问一边记录相关价格信息。

这里我记录的有:vis[i]标记i是否被询问过,rt[i]最近的关于点i的询问中i的祖先,minp[i]为i到rt[i]之间的最小价格,maxp[i]为i到rt[i]之间的最大价格,up[i]为从i到rt[i]之间的可赚取的最大收益,down[i]为从rt[i]到i之间的最大收益。

在询问(u,v)中,求得这些信息时,按朴素的想法从u向上到LCA(u,v),再从v到LCA(u,v)遍历一遍,遍历时遇到询问过的点i,根据那些minp,maxp,up,down什么的,特殊处理一下后可直接跳到rt[i]的父节点去,那么所有的点访问次数就降下很多了。

得到那些信息后,询问的答案也就出来了,有ans=max{up[u],down[v],maxp[v]-minp[u]},注意LCA(u,v)==u或者LCA(u,v)==v的情况。

//还有不明白的见代码

整个的分析过程花去了太多时间,还好是1Y。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <queue>
#include <algorithm>
#define Root 1
using namespace std;

const int NN=150000;
const int INF=0x3fffffff;

int en,head[NN];
struct Edge
{
    int v,next;
} e[NN];
void add(int u,int v)
{
    e[en].v=v;
    e[en].next=head[u];
    head[u]=en++;
    e[en].v=u;
    e[en].next=head[v];
    head[v]=en++;
}

int fa[NN],num[NN];
bool vis[NN];
void bfs()
{
    int u,v,i,cnt;

    memset(vis,0,sizeof(vis));
    num[Root]=vis[Root]=cnt=1;
    fa[Root]=-1;
    queue<int> q;
    q.push(Root);
    while (!q.empty())
    {
        u=q.front();
        q.pop();
        for (i=head[u]; i!=-1; i=e[i].next)
        {
            v=e[i].v;
            if (vis[v]) continue;
            vis[v]=true;
            num[v]=++cnt;
            fa[v]=u;
            q.push(v);
        }
    }
}

int bn,depth,b[NN],f[NN],at[NN];
void dfs(int u)
{
    int tmp=++depth;
    b[++bn]=tmp; f[tmp]=u; at[u]=bn;
    for (int i=head[u]; i!=-1; i=e[i].next)
    {
        int v=e[i].v;
        if (fa[u]==v) continue;
        dfs(v);
        b[++bn]=tmp;
    }
}

int dp[NN][30];
void LCA_init()
{
    bn=depth=0;
    dfs(Root);
    for (int i=1; i<=bn; i++) dp[i][0]=b[i];
    int m=floor(log(bn*1.0)/log(2.0));
    for (int j=1; j<=m; j++)
        for (int i=1; i<=bn-(1<<j)+1; i++)
            dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}

int rmq(int l,int r)
{
    int k=floor(log((r-l+1)*1.0)/log(2.0));
    return min(dp[l][k],dp[r-(1<<k)+1][k]);
}

int LCA(int a,int b)
{
    if (at[a]>at[b]) swap(a,b);
    int k=rmq(at[a],at[b]);
    return f[k];
}

int price[NN],ans[NN],rt[NN],minp[NN],maxp[NN],up[NN],down[NN];
void get(int v,int x)
{
    if (vis[v] && rt[v]==x) return;
    int u=v;
    int a1,a2,a3,b1,b2,b3,c1,c2;//a1b1c1求up用,a2b2c2求down用,a3b3求minp和maxp用
    a1=a2=a3=INF; b1=b2=b3=0; c1=c2=0;
    while (u!=fa[x])
    {
        if (vis[u])
        {
            if (up[u]>c1)   c1=up[u];
            if (down[u]>c2) c2=down[u];
            if (maxp[u]-a1>c1) c1=maxp[u]-a1;
            if (minp[u]<a1) a1=b1=minp[u];
            if (b2-minp[u]>c2) c2=b2-minp[u];
            if (maxp[u]>b2) a2=b2=maxp[u];
            if (minp[u]<a3) a3=minp[u];
            if (maxp[u]>b3) b3=maxp[u];
            u=fa[rt[u]];
        }
        else
        {
            if (price[u]<a1) a1=b1=price[u];
            if (price[u]>b1) b1=price[u];
            if (price[u]>b2) a2=b2=price[u];
            if (price[u]<a2) a2=price[u];
            if (price[u]<a3) a3=price[u];
            if (price[u]>b3) b3=price[u];
            u=fa[u];
        }
        if (b1-a1>c1) c1=b1-a1;
        if (b2-a2>c2) c2=b2-a2;
    }
    minp[v]=a3; maxp[v]=b3;
    up[v]=c1;   down[v]=c2;
    rt[v]=x;
    vis[v]=true;
}
struct question
{
    int u,v,x,id;
    bool operator <(const question a)const
    {
        return num[x]>num[a.x];
    }
} que[NN];
int main()
{
    int i,u,v,n,q,x;

    scanf("%d",&n);
    en=0;
    for (i=1; i<=n; i++)
    {
        scanf("%d",&price[i]);
        head[i]=-1;
    }
    for (i=1; i<n; i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    bfs();
    LCA_init();

    scanf("%d",&q);
    for (i=1; i<=q; i++)
    {
        scanf("%d%d",&u,&v);
        que[i].u=u;
        que[i].v=v;
        que[i].x=LCA(u,v);
        que[i].id=i;
    }
    sort(que+1,que+1+q);
    memset(vis,0,sizeof(vis));
    for (i=1; i<=q; i++)
    {
        u=que[i].u;
        v=que[i].v;
        x=que[i].x;

        get(u,x);
        get(v,x);
        if (u==x)      ans[que[i].id]=down[v];
        else if (v==x) ans[que[i].id]=up[u];
        else           ans[que[i].id]=max(max(up[u],down[v]),maxp[v]-minp[u]);
    }
    for (i=1; i<=q; i++) printf("%d\n",ans[i]);

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值