[SPOJ1825]免费旅行II

该博客主要介绍了SPOJ的一道算法题,题目要求在ICPC岛屿的旅行中,找到一条在最多访问K个拥挤点的条件下,权值和最大的路径。博主探讨了利用树状数组解决该问题的策略,包括如何存储和更新数据,以及在点分治中应用树状数组的细节。
摘要由CSDN通过智能技术生成

传送门

题目描述

在两周年纪念日的旅行之后,在第三年,旅行社SPOJ又一次踏上的打折旅行的道路。

这次旅行是ICPC岛屿上进行的,一个位于太平洋上,不可思议的小岛。我们列出了N个地点(编号从1到N)供旅客游览。这N个点由N-1条边连成一个树,每条边都有一个权值,这个权值可能为负。我们可以选择两个地点作为旅行的起点和终点。

由于当地正在庆祝节日,所以某些地方会特别的拥挤(我们称这些地方为拥挤点)。旅行的组织者希望这次旅行最多访问K个拥挤点。同时,我们希望我们经过的道路的权值和最大。

输入格式
第一行,三个整数N,K,M(1 <= N <= 200000, 0 <= K <=M, 0 <= M <= N)

之后的M行,每行一个拥挤点的编号。

最后的N-1行,每行三个整数u,v,l,代表u和v之间有一条权值为l的边

输出格式

一个整数,权值和最大的旅行线路

样例数据

input

8 2 3
3
5
7
1 3 1
2 3 10
3 4 -2
4 5 -1
5 7 6
5 6 5
4 8 3

output

12

解题思路

题目要求的是最多访问K个拥挤点的前提下,经过的道路的权值和最大,因为让求的是一个最大值,显然不满足可减性,不考虑使用容斥的方法。后面的求道路的权值和最大并不是什么大问题,问题就在于拥挤点这个条件。

因为 K<=M<=N ,所以可以考虑用树状数组(原因稍后口胡)将拥挤点的个数当做下标,道路的最大权值当做值,把当前重心先其他子树的值存在树状数组里,然后在当前子树内点到重点距离存在p数组中,用p数组和树状数组更新ans(记得做完当前重心的所有子树后要清空树状数组):

ans= max(ans,query(k-p[j].sum+1)+p[j].dis); //sum是当前路径拥挤点的个数,dis是路径长度

其实上面所做的操作就是原来的get函数,要注意因为树状数组中存的是最大权值,所以在更新或询问时只取max,而不是原来的加法。剩余的部分正常按照点分治去写就行了

关于树状数组 ,在这里仅给出我个人的观点,这里tr[i]的i其实代表的是前面路径中最多有i个拥挤点时,道路的最大权值为多少,所以当我们又求出了恰好有i个拥挤点时的道路的权值,相应的也要在tr[i~k]中进行更新。

代码

#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;

const int N=2e5+10;
int pt,qt,len,l[N],st[N],tr[N];
int n,k,m,f[N],ans=-0x3f3f3f3f;//f数组标记当前点是不是拥挤点,在后面选择把f数组加起来来计算路径中拥挤点的总个数

struct nod
{
    int y;
    int w;
    int nxt;
}e[N*2];

struct node
{
    int dis;//路径的最大权值
    int sum;//路径中拥挤点的个数
}p[N],q[N];//q为所有子树中的路径,p为当前子树中的路径

inline void modify(int x,int y)
{
    for(;x<=n;x+=lowbit(x))
    tr[x]=max(tr[x],y);
}

inline int query(int x)
{
    int res=-10100;
    for(;x;x-=lowbit(x))
    res=max(res,tr[x]);
    return res;
}

inline void clea(int x)
{
    for(;x<=n;x+=lowbit(x))
    tr[x]=0;//删除重心后要清空原来的值
}

inline void Insert(int xx,int yy,int ww)
{
    e[++len].nxt=l[xx];
    l[xx]=len;
    e[len].y=yy;
    e[len].w=ww;
}

inline int get_size(int u,int fa)
{
    if(st[u]) return 0;
    int res=1;
    for(int i=l[u];i;i=e[i].nxt)
    {
        int y=e[i].y;
        if(y==fa) continue;
        res+=get_size(y,u);
    }
    return res;
}

inline int get_wc(int u,int fa,int tot,int &wc)
{
    if(st[u]) return 0;
    int sum=1,ms=-10010;
    for(int i=l[u];i;i=e[i].nxt)
    {
        int y=e[i].y;
        if(y==fa) continue;
        int t=get_wc(y,u,tot,wc);
        ms=max(ms,t);
        sum+=t;
    }
    ms=max(ms,tot-sum);
    if(ms<=tot/2) wc=u;
    return sum;
}

inline void get_dist(int u,int fa,int dist,int sonsum,int fasum)//sonsum是子节点中拥挤点的个数
{
    if(st[u]|| sonsum>k-fasum) return;
    p[++pt].dis=dist;
    p[pt].sum=sonsum;
    for(int i=l[u];i;i=e[i].nxt)
    {
        int y=e[i].y;
        if(y==fa) continue ;
        get_dist(y,u,dist+e[i].w,sonsum+f[y],fasum);
    }
}

inline void calc(int u)
{
    if(st[u]) return;
    get_wc(u,0,get_size(u,0),u);
    st[u]=1;
    qt=0;
    for(int i=l[u];i;i=e[i].nxt)
    {
        pt=0;
        get_dist(e[i].y,u,e[i].w,f[e[i].y],f[u]);
        for(int j=1;j<=pt;j++)
        {
            ans=max(ans,query(k-p[j].sum+1)+p[j].dis);//更新ans
            q[++qt]=p[j];
        }
        for(int j=1;j<=pt;j++) modify(p[j].sum+f[u]+1,p[j].dis);
    }

    for(int i=1;i<=qt;i++) clea(q[i].sum+f[u]+1);
    for(int i=l[u];i;i=e[i].nxt) calc(e[i].y);
}

int main()
{
    freopen("freetourII.in","r",stdin);
	freopen("freetourII.out","w",stdout);
    scanf("%d%d%d",&n,&k,&m);
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%d",&x);
        f[x]=1;
    }
    for(int i=1;i<n;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        Insert(u,v,w);
        Insert(v,u,w);
    }
    calc(1);
    if(ans==-0x3f3f3f3f) ans=0;
    printf("%d",ans);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值