HDU 5770 Treasure

2 篇文章 0 订阅
1 篇文章 0 订阅

给定一棵树,树上有m个宝箱,每个宝箱都有一个把钥匙,以及各自的价值,要先拿到钥匙才可以开宝箱,必须开掉所有能开的宝箱。要求选择一条简单路径,使得最后获得的总价值最大。

官方题解:

假设钥匙在节点 A ,宝箱在节点B C=LCA(A,B) ,则可以分四种情况讨论

1. CA,CB 对于这种情况,只要起点在以 A 为根的子树中,终点在以B为根的子树中,都可以拿到这份宝藏,而子树 A 中的所有节点dfs序连续,子树B同理,于是我们可以用一个矩阵表示能取得该宝藏的所有方案。

2. C=A,AB 对于这种情况,需要先求出节点 D D为路径 (A,B) 上最靠近 A 的节点,那么只要终点在子树B上,起点不在子树 D 上的路径,都可以拿到这份宝藏,而不在子树D上的点,可以用一个或者两个dfs序区间表示,因此可以用最多两个矩阵表示能取得该宝藏的所有方案。

3. C=B,AB 和情况2类似。

4. A=B 对于这种情况,若要求出所有经过节点 A 的路径,矩阵数目会是n2级别的,因此反过来思考,求出所有不包含节点 A 的路径,对于全部这种情况来说这样矩阵数目的级别为n。可以对答案先累加宝藏权值,然后对于所有不经过该点的矩阵减去这部分权值即可。

dfs序+扫描线(线段树)

#include<set>
#include<ctime>
#include<queue>
#include<cstdio>
#include<bitset>
#include<cctype>
#include<bitset>
#include<cstdlib>
#include<cassert>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf (1<<30)
#define INF (1ll<<62)
#define fi first
#define se second
#define rep(x,s,t) for(int x=s,t_=t;x<t_;x++)
#define per(x,s,t) for(int x=t-1,s_=s;x>=s_;x--)
#define prt(x) cout<<#x<<":"<<x<<" "
#define prtn(x) cout<<#x<<":"<<x<<endl
#define pc(x) putchar(x)
#define pb(x) push_back(x)
#define hash asfmaljkg
#define rank asfjhgskjf
#define y1 asggnja
#define y2 slfvm
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
template<class T>void sc(T &x){
    int f=1;char c;x=0;
    while(c=getchar(),c<48)if(c=='-')f=-1;
    do x=x*10+(c^48);
    while(c=getchar(),c>47);
    x*=f;
}
template<class T>void nt(T x){
    if(!x)return;
    nt(x/10);
    pc(x%10+'0');
}
template<class T>void pt(T x){
    if(x<0)pc('-'),x=-x;
    if(!x)pc('0');
    else nt(x);
}
template<class T>void ptn(T x){
    pt(x);putchar('\n');
}
template<class T>void pts(T x){
    pt(x);putchar(' ');
}
template<class T>inline void Max(T &x,T y){if(x<y)x=y;}
template<class T>inline void Min(T &x,T y){if(x>y)x=y;}

const int maxn=100005;
int n,m;
int lx[maxn],rx[maxn],dfs_clock;
int link[maxn];

int last[maxn],ecnt;
struct Edge{
    int to,nxt;
}e[maxn<<1];
inline void ins(int u,int v){
    e[ecnt]=(Edge){v,last[u]};
    last[u]=ecnt++;
}

int tot;
struct unit{
    int x,l,r,v;
    inline bool operator<(const unit&a)const{
        return x<a.x;
    }
}tar[maxn*3];

int sz[maxn],par[maxn],dep[maxn];
void assign(int x,int f){
    sz[x]=1;
    par[x]=f;
    dep[x]=dep[f]+1;

    for(int i=last[x];i!=-1;i=e[i].nxt){
        int to=e[i].to;
        if(to==f)continue;
        assign(to,x);
        sz[x]+=sz[to];
    }
}

void dfs(int x,int p){
    int mx=0;
    link[x]=p;
    lx[x]=rx[x]=++dfs_clock;

    for(int i=last[x];i!=-1;i=e[i].nxt){
        int to=e[i].to;
        if(to==par[x])continue;
        if(sz[to]>sz[mx])mx=to;
    }
    if(!mx)return;
    dfs(mx,p);
    for(int i=last[x];i!=-1;i=e[i].nxt){
        int to=e[i].to;
        if(to==par[x])continue;
        if(to!=mx)dfs(to,to);
    }
    rx[x]=dfs_clock;
}

int lca(int x,int y){
    while(link[x]!=link[y]){
        if(dep[link[x]]<dep[link[y]])swap(x,y);
        x=par[link[x]];
    }
    return dep[x]<dep[y]?x:y;
}

void add_rec(int a1,int a2,int b1,int b2,int val){
//  [a1,a2][b1,b2]
//  prt(a1);prt(a2);prt(b1);prt(b2);prtn(val);
    tar[++tot]=(unit){a1,b1,b2,val};
//  prtn(tar[tot].r);
    tar[++tot]=(unit){a2+1,b1,b2,-val};
//  prtn(tar[tot].r);
}

struct node{
    int mx,flag;
};

struct Segment{
    node v[maxn<<2];
    void down(int x){
        if(v[x].flag){
            adjust(x<<1,v[x].flag);
            adjust(x<<1|1,v[x].flag);
            v[x].flag=0;
        }
    }
    inline void up(int x){
        v[x].mx=max(v[x<<1].mx,v[x<<1|1].mx);
    }
    inline void adjust(int x,int flag){
        v[x].mx+=flag;
        v[x].flag+=flag;
    }
    void update(int l,int r,int x,int L,int R,int val){
        if(L<=l&&r<=R){
            adjust(x,val);
            return;
        }
        int mid=l+r>>1;
        down(x);
        if(L<=mid)update(l,mid,x<<1,L,R,val);
        if(R>mid)update(mid+1,r,x<<1|1,L,R,val);
        up(x);
    }
    void build(int l,int r,int x){
        v[x].mx=v[x].flag=0;
        if(l==r)return;
        int mid=l+r>>1;
        build(l,mid,x<<1);
        build(mid+1,r,x<<1|1);
    }
    int maximum(){
        return v[1].mx;
    }
}sgm;

void init(){
    memset(last,-1,n+1<<2);
    ecnt=0;
    dfs_clock=0;
    tot=0;
    sgm.build(1,n,1);
}
int all;
#define get got
int get(int x,int y){
    for(int i=last[x];i!=-1;i=e[i].nxt){
        int to=e[i].to;
        if(to==par[x])continue;
        if(lx[to]<=lx[y]&&rx[y]<=rx[to])
            return to;
    }
    return 0;
}
void update(int u,int v,int x){
    int w=lca(u,v);
    if(w!=u&&w!=v)
        add_rec(lx[u],rx[u],lx[v],rx[v],x);
    else if(w!=u){//w==v
        int k=get(v,u);
        if(rx[k]<n)add_rec(lx[u],rx[u],rx[k]+1,n,x);
        if(lx[k]>1)add_rec(lx[u],rx[u],1,lx[k]-1,x);
    }
    else if(w!=v){//w==u
        int k=get(u,v);
        if(rx[k]<n)add_rec(rx[k]+1,n,lx[v],rx[v],x);
        if(lx[k]>1)add_rec(1,lx[k]-1,lx[v],rx[v],x);
    }
    else{
        all+=x;
        for(int i=last[u];i!=-1;i=e[i].nxt){
            int to=e[i].to;
            if(to==par[u])continue;
            add_rec(lx[to],rx[to],lx[to],rx[to],-x);
        }
        if(lx[u]>1)add_rec(1,lx[u]-1,1,lx[u]-1,-x);
        if(rx[u]<n)add_rec(rx[u]+1,n,rx[u]+1,n,-x);
        if(lx[u]>1&&rx[u]<n){
            add_rec(1,lx[u]-1,rx[u]+1,n,-x);
            add_rec(rx[u]+1,n,1,lx[u]-1,-x);
        }
    }
}

int ans;
void work(){
    ans=-inf;
    sort(tar+1,tar+tot+1);
    int top=1;
    rep(i,1,n+1){
        while(top<=tot&&tar[top].x==i){
            int l=tar[top].l;
            int r=tar[top].r;
            int v=tar[top].v;
            sgm.update(1,n,1,l,r,v);
            top++;
        }
        Max(ans,sgm.maximum());
    }
}

void solve(){
    sc(n);sc(m);
    init();
    int u,v;
    rep(i,1,n){
        sc(u);sc(v);
        ins(u,v);
        ins(v,u);
    }
    assign(1,0);
    dfs(1,1);

    int w;
    all=0;
    rep(i,0,m){
        sc(u);sc(v);sc(w);
        update(u,v,w);
    }
    work();
    ptn(ans+all);
}
int main(){
//  freopen("pro.in","r",stdin);
//  freopen("pro.out","w",stdout);
    int cas;sc(cas);
    rep(kase,1,cas+1){
        printf("Case #%d: ",kase);
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值