HDU5770 Treasure - LCA - 扫描线 - 线段树

8 篇文章 0 订阅
3 篇文章 0 订阅

传送门

题目大意:……给定一棵树,有若干三元组(u,v,w)表示有一个宝箱在v,其唯一对应的钥匙在v,且宝箱价值是w(可能为负)。

让你选一条简单路径(有向),每到一个点要求必须先拿起钥匙(如果有的话),然后必须打开宝箱(如果有宝箱并且手上已经有钥匙了),最大化价值之和。

题解:思路过程如下:直接统计每条路径的价值没有办法。

显然一条路径由起点和终点唯一决定,下文记为(s,t)。我们可以考虑统计vi这个宝箱会使得哪些(s,t)价值+=w。

直观来看,起点必须在u的子树中,终点必须在v的子树中,“子树”提示我们dfs序,也就是w会对起点在[in[u],out[u]](in和out表示u的子树在dfs序上的两端),终点在[in[v],out[v]]的路径产生影响,因此这一部分要+w。如果我们把起点看成横坐标,终点看作纵坐标,那么(s,t)就对应了一个点,而([in[u],out[u]],in[v],out[v])对应了一个矩形。

于是我们要做的就是维护数据结构支持矩阵加一个数和查询矩阵中的最大值。这个东西显然可以用二维线段树来维护,但是事实表明无论你怎么优化空间都会MLE。

考虑这个数据结构要维护的事情的特殊性质:只查询依次并且是先所有修改完了之后才查询。

这提示我们可以先离线;线段树的离线手段显然可以考虑扫描线降维,因此我们枚举横坐标,由于每个横坐标,把所有左边界在这里的矩阵加上对应的高;

这个横坐标全部加完了之后统计最大值。然后在右边界+1的位置消除影响。(可以认为是在out[u]+1处的[in[v],out[v]]加了一个-w)。

这样由于每个矩阵都被考虑了常数次所以复杂度O(nlgn)。至于为什么是对的,一个点(x,y)总是不会被边界在它右边的点影响。


但是这么交上去,,,WA的可惨可惨了。

然后拍几组数据发现少考虑了一些情况,例如如果u是v的祖先,或者v是u的祖先,以及u和v是同一个点的情况需要特殊处理。

怎么判断一个点是都是另一个点的祖先呢?求一下类似于LCA的倍增数组即可。

先考虑u=v的情况。这种情况下,跨越u的儿子子树或者从u的父亲处来到u的子树中的路径或者从u出发/结束的路径都被统计到了。

显然这么做麻烦无比一不小心就会统计错。

考虑用全部路径减去不经过u的路径。显然全部路径就是起点在[1,n],终点在[1,n]的矩形,这是废话。

哪些不会经过u呢?分为两类,第一类是s和t都在u的同一颗儿子子树中,即s∈[in[son[u]],out[son[u]]],t∈[in[son[u]],out[son[u]]];

第二类是s和t都不在u的子树中;这部分怎么统计呢?当然可以统计,但是太麻烦了。

因此转换一下思路,我们一开始就不统计s和t都不在u的子树中的情况。

我们把全部路径的部分设置成“s在u的子树中,t在随便一个地方”。

那么显然依旧多了第一部分,不影响。

然后发现对于第二部分,还少了s不在u的子树中,而t在u的子树中的部分。

这个可以用s在1到n中,t在u的子树中的情况,减去s在u的子树中,t在u的子树中的情况即可。

u是v的祖先和v是u的祖先情况类似,只考虑第一种。

那么终点依旧是v的子树中,毫无疑问。

但是起点就不能是在u的儿子节点lv的子树中,其中lv的子树是v所在子树。lv可以用LCA求。

用上述方法减去即可。讨论到这里其实也就看出来问什么要单独讨论出u是v的祖先(以及反过来)

以及为什么讨论完这个还要特判u和v是同一个点的情况。(应为u和v是同一个点就不存在lv这个点)。


PS:一开始还想用一个类似于动态开点四分树的东西维护但是最终MLE(或者WA/RE)了。

感觉这个题还是挺好的,就是讨论麻烦了些。


#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAXM 200010
#define MAXN 100010
#define MAXL 20
#define MAXS 2000010
using namespace std;
struct edges{
    int to,pre;
}e[MAXM];
int h[MAXN],hs[MAXN],etop,top;
int in[MAXN],out[MAXN],dfs_clock;
int up[MAXN][MAXL],dep[MAXN],val[MAXN];
inline int add_edge(int u,int v)
{
    etop++;
    e[etop].to=v;
    e[etop].pre=h[u];
    h[u]=etop;
    return 0;
}
int dfs(int x,int fa)
{
    in[x]=++dfs_clock;
    up[x][0]=fa,dep[x]=dep[fa]+1;
    for(int i=1;i<MAXL;i++)
        up[x][i]=up[up[x][i-1]][i-1];
    for(int i=h[x];i;i=e[i].pre)
        if(e[i].to^fa) dfs(e[i].to,x);
    out[x]=dfs_clock;return 0;
}
inline int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=MAXL-1;i>=0;i--)
        if(dep[up[x][i]]>=dep[y])
            x=up[x][i];
    if(x==y) return x;
    for(int i=MAXL-1;i>=0;i--)
        if(up[x][i]!=up[y][i])
            x=up[x][i],y=up[y][i];
    return up[x][0];
}
inline int getLCA(int u,int v,int &lu,int &lv)
{
    int c=LCA(u,v);
    for(int i=MAXL-1;i>=0;i--)
        if(dep[up[u][i]]>dep[c])
            u=up[u][i];
    for(int i=MAXL-1;i>=0;i--)
        if(dep[up[v][i]]>dep[c])
            v=up[v][i];
    lu=u;lv=v;return c;
}
struct Seg{
    int u,d,pre,w;
}seg[MAXS];
inline int add_seg(int id,int u,int d,int w)
{
    top++;
//    cout<<id<<" "<<u<<" "<<d<<" "<<w<<endl;
    seg[top].u=u;
    seg[top].d=d;
    seg[top].w=w;
    seg[top].pre=hs[id];
    hs[id]=top;
    return 0;
}
struct segment{
    int l,r,maxn,pt;
    segment *ch[2];
}*rt;
inline int update_tags(segment* &rt,int v)
{
    rt->pt+=v;rt->maxn+=v;
    return 0;
}
inline int push_down(segment* &rt)
{
    update_tags(rt->ch[0],rt->pt);
    update_tags(rt->ch[1],rt->pt);
    rt->pt=0;return 0;
}
inline int push_up(segment* &rt)
{
    return rt->maxn=max(rt->ch[0]->maxn,rt->ch[1]->maxn);
}
inline int build(segment* &rt,int l,int r)
{
    rt=new segment;
    rt->l=l,rt->r=r;
    rt->maxn=rt->pt=0;
    rt->ch[0]=rt->ch[1]=NULL;
    if(l==r) return 0;
    int mid=(l+r)>>1;
    build(rt->ch[0],l,mid);
    build(rt->ch[1],mid+1,r);
    return 0;
}
inline int update(segment* &rt,int s,int t,int v)
{
    int l=rt->l,r=rt->r;
    if(s<=l&&r<=t) return update_tags(rt,v);
    if(rt->pt) push_down(rt);
    int mid=(l+r)>>1;
    if(s<=mid) update(rt->ch[0],s,t,v);
    if(mid<t) update(rt->ch[1],s,t,v);
    push_up(rt);return 0;
}
int main()
{
    int T;scanf("%d",&T);
    int test_cnt=0;
    while(T--)
    {
        int n,m;scanf("%d%d",&n,&m);
        memset(h,0,sizeof(h)),etop=0;
        memset(hs,0,sizeof(hs)),top=0;
        memset(val,0,sizeof(val));
        for(int i=1;i<n;i++)
        {
            int u,v;scanf("%d%d",&u,&v);
            add_edge(u,v),add_edge(v,u);
        }
        int root=1;dfs_clock=0;rt=NULL;
        dfs(root,0);build(rt,1,n);
//        cout<<endl;for(int i=1;i<=n;i++) cout<<in[i]<<" "<<out[i]<<endl;cout<<endl;
        while(m--)
        {
//            cout<<"-----------------";
            int u,v,w,lu,lv;scanf("%d%d%d",&u,&v,&w);
            int c=getLCA(u,v,lu,lv);
//            cout<<c<<" "<<u<<" "<<v<<" "<<lu<<" "<<lv<<endl;
            if(u==v)
            {
            /*    cout<<"1:";add_seg(in[u],1,n,w);
                cout<<"2:";add_seg(out[u]+1,1,n,-w);
                cout<<"3:";add_seg(in[u],in[u],out[u],-w);
                cout<<"4:";add_seg(out[u]+1,in[u],out[u],w);
                cout<<"5:";add_seg(in[u],in[u],in[u],w);
                cout<<"6:";add_seg(out[u]+1,in[u],in[u],-w);
                cout<<"7:";add_seg(in[u],in[u],out[u],w);
                cout<<"8:";add_seg(in[u]+1,in[u],out[u],-w);
                cout<<"9:";add_seg(in[u],in[u],in[u],-w);
                cout<<"10:";add_seg(in[u]+1,in[u],in[u],w);
                
                cout<<"11:";add_seg(1,in[u],out[u],w);
                cout<<"12:";add_seg(in[u],in[u],out[u],-w);
                cout<<"13:";add_seg(out[u]+1,in[u],out[u],w);*/
            /*    add_seg(in[u],1,n,w);
                add_seg(in[u]+1,1,n,-w);
                add_seg(1,in[u],in[u],w);
                add_seg(n+1,in[u],in[u],-w);
                add_seg(in[u],in[u],in[u],-w);
                add_seg(in[u]+1,in[u],in[u],w);*/
                val[u]+=w;
            }
            else if(c==u)
            {
                add_seg(1,in[v],out[v],w);
                add_seg(n+1,in[v],out[v],-w);
                add_seg(in[lv],in[v],out[v],-w);
                add_seg(out[lv]+1,in[v],out[v],w);
            }
            else if(c==v)
            {
                add_seg(in[u],1,n,w);
                add_seg(out[u]+1,1,n,-w);
                add_seg(in[u],in[lu],out[lu],-w);
                add_seg(out[u]+1,in[lu],out[lu],w);
            }
            else{
                add_seg(in[u],in[v],out[v],w);
                add_seg(out[u]+1,in[v],out[v],-w);
            }
        }
        for(int i=1;i<=n;i++)
            if(val[i])
            {
                add_seg(in[i],1,n,val[i]);
                add_seg(out[i]+1,1,n,-val[i]);
                for(int j=h[i];j;j=e[j].pre)
                    if(e[j].to^up[i][0])
                    {
                        add_seg(in[e[j].to],in[e[j].to],out[e[j].to],-val[i]);
                        add_seg(out[e[j].to]+1,in[e[j].to],out[e[j].to],val[i]);
                    }
                    else{
                        add_seg(1,in[i],out[i],val[i]);
                        add_seg(n+1,in[i],out[i],-val[i]);
                        add_seg(in[i],in[i],out[i],-val[i]);
                        add_seg(out[i]+1,in[i],out[i],val[i]);
                    }
            }
//        for(int i=1;i<=top;i++) cout<<seg[i].u<<" "<<seg[i].d<<" "<<seg[i].w<<endl;
        int ans=INT_MIN;
        for(int i=1;i<=n;i++)
        {
            for(int j=hs[i];j;j=seg[j].pre)
            {
                update(rt,seg[j].u,seg[j].d,seg[j].w);
//                cout<<seg[j].u<<" "<<seg[j].d<<" "<<seg[j].w<<endl;
            }
            ans=max(ans,rt->maxn);
        }
        printf("Case #%d: %d\n",++test_cnt,ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值