bzoj 3252: 攻略

题意

众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏。今天他得到了一款新游戏《XX半岛》,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景。所有场景和选择支构成树状结构:开始游戏时在根节点(共通线),叶子节点为结局。每个场景有一个价值,现在桂马开启攻略之神模式,同时攻略k次该游戏,问他观赏到的场景的价值和最大是多少(同一场景观看多次是不能重复得到价值的)

题解

首先,你仔细想一想就可以得到一个结论,那就是每一次肯定是选择代价最大的点,这样肯定是最优的,我们就来考虑维护这一个过程、

解法1

直接dfs序,然后线段树维护一下就可以了
注意一下,每个点最多被修改一次,那么在你删点的时候暴力跳就可以了

CODE:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;
const LL N=200005*2;
LL n,k;
LL A[N];
struct qq
{
    LL x,y,last;
}e[N];LL num,last[N];
void init (LL x,LL y)
{
    num++;
    e[num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
LL L[N],R[N],cnt=0;
LL belong[N];
bool vis[N];
LL dep[N];
struct qr
{
    LL l,r;
    LL s1,s2;
    LL c,c1;
    LL lazy;
}tr[N*2];
LL ff[N];
void dfs (LL x,LL fa)
{
    L[x]=++cnt;belong[cnt]=x;
    for (LL u=last[x];u!=-1;u=e[u].last)
    {
        LL y=e[u].y;
        if (y==fa) continue;
        dep[y]=dep[x]+A[y];ff[y]=x;dfs(y,x);
    }
    R[x]=cnt;
}
void update (LL x)
{
    LL s1=tr[x].s1,s2=tr[x].s2;
    if (tr[s1].c>tr[s2].c)
    {
        tr[x].c=tr[s1].c;
        tr[x].c1=tr[s1].c1;
    }
    else
    {
        tr[x].c=tr[s2].c;
        tr[x].c1=tr[s2].c1;
    }
}
void bt (LL l,LL r)
{
    LL a=++num;
    tr[a].l=l;tr[a].r=r;
    if (l==r)
    {
        tr[a].c=dep[belong[l]];
        tr[a].c1=belong[l];
        return ;
    }
    LL mid=(l+r)>>1;
    tr[a].s1=num+1;bt(l,mid);
    tr[a].s2=num+1;bt(mid+1,r);
    update(a);
}
void push_down (LL x)
{
    LL s1=tr[x].s1,s2=tr[x].s2;
    tr[s1].c+=tr[x].lazy;
    tr[s1].lazy+=tr[x].lazy;
    tr[s2].c+=tr[x].lazy;
    tr[s2].lazy+=tr[x].lazy;
    tr[x].lazy=0;
}
void change (LL now,LL l,LL r,LL x)
{
    /*printf("%lld %lld %lld %lld %lld\n",tr[now].l,tr[now].r,l,r,x);
    system("pause");*/
    if (tr[now].l==l&&tr[now].r==r)
    {
        tr[now].c-=x;
        tr[now].lazy-=x;
        return ;
    }
    push_down(now);
    LL s1=tr[now].s1,s2=tr[now].s2;
    LL mid=(tr[now].l+tr[now].r)>>1;
    if (r<=mid) change(s1,l,r,x);
    else if (l>mid) change(s2,l,r,x);
    else change(s1,l,mid,x),change(s2,mid+1,r,x);
    update(now);
}
void solve (LL x)
{
    while (x!=0)
    {
        if (vis[x]==true) return ;
        vis[x]=true;
    //  printf("%lld %lld %lld\n",x,L[x],R[x]);
        change(1,L[x],R[x],A[x]);
        x=ff[x];
    }
}
int main()
{
    memset(vis,false,sizeof(vis));
    LL ans=0;
    num=0;memset(last,-1,sizeof(last));
    scanf("%lld%lld",&n,&k);
    for (LL u=1;u<=n;u++) scanf("%lld",&A[u]);
    for (LL u=1;u<n;u++)
    {
         LL x,y;
         scanf("%lld%lld",&x,&y);
         init(x,y);init(y,x);
    }
    dep[1]=A[1];dfs(1,0);
    num=0;bt(1,n);
    for (LL u=1;u<=k;u++)
    {
    //  printf("%lld %lld\n",u,tr[1].c);
        ans=ans+tr[1].c;
        solve(tr[1].c1);
    }
    printf("%lld\n",ans);
    return 0;
}

解法2

明显地长链剖分模型的啊。。
直接长链剖分就可以了。。
虽然我没有看出来这是长链剖分

Code

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
typedef long long LL;
const LL N=200005*2;
LL n,k;
LL A[N];
struct qq
{
    LL x,y,last;
}e[N];LL num,last[N];
void init (LL x,LL y)
{
    num++;
    e[num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
bool vis[N];
LL dep[N];
LL mx[N];
LL son[N];
LL ff[N];
void dfs (LL x,LL fa)
{
    mx[x]=dep[x];
    for (LL u=last[x];u!=-1;u=e[u].last)
    {
        LL y=e[u].y;
        if (y==fa) continue;
        dep[y]=dep[x]+A[y];ff[y]=x;dfs(y,x);
        mx[x]=max(mx[x],mx[y]);
        if (mx[y]>mx[son[x]]) son[x]=y;
    }
}
priority_queue<LL> q;
void Dfs (LL x,LL sum)
{
    if (son[x]!=0)  Dfs(son[x],sum+A[son[x]]);
    else q.push(sum);
    for (LL u=last[x];u!=-1;u=e[u].last)
    {
        LL y=e[u].y;
        if (y==ff[x]||y==son[x]) continue;
        Dfs(y,A[y]);
    }
}
int main()
{
    memset(vis,false,sizeof(vis));
    LL ans=0;
    num=0;memset(last,-1,sizeof(last));
    scanf("%lld%lld",&n,&k);
    for (LL u=1;u<=n;u++) scanf("%lld",&A[u]);
    for (LL u=1;u<n;u++)
    {
         LL x,y;
         scanf("%lld%lld",&x,&y);
         init(x,y);init(y,x);
    }
    dep[1]=A[1];dfs(1,0);
    Dfs(1,A[1]);
    while (k!=0&&(!q.empty()))
    {
        ans=ans+q.top();
        q.pop();
        k--;
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值