spoj 1825 Free tour II(树分治+启发式合并)

题意:
有N个顶点的树,节点间有权值, 节点分为黑点和白点。 找一条最长路径使得 路径上黑点数量不超过K个。


思路:
设当前节点x到根的路径上黑色节点数量为deep[x],路径长度为dis[x]
将子节点按照最大deep倒序处理,利用启发式合并使得合并复杂度降为nlogn



#include<bits/stdc++.h>
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
using namespace std;
const int N=2e5+100;
int K,head[N],tot;
struct Edge{
    int to,next,w;
}e[N*2];
int size[N],f[N],Count,root,Color[N];
bool Del[N];

void addedge(int from,int to,int w){
    e[tot]=(Edge){to,head[from],w};
    head[from]=tot++;
}

void getroot(int u,int fa){
    size[u]=1,f[u]=0;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(v!=fa&&!Del[v]){
            getroot(v,u);
            size[u]+=size[v],gmax(f[u],size[v]);
        }
    }
    gmax(f[u],Count-size[u]);
    if(f[root]>f[u])    root=u;
}

int ans;
struct node{
    int first,second;
}st[N];
int deep[N],dis[N],deep_mx,tmp[N],mx[N];

void getdeep(int u,int fa,int dep,int d){
    deep[u]=dep+Color[u],dis[u]=d;
    gmax(deep_mx,deep[u]);
    size[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(!Del[v]&&v!=fa){
            getdeep(v,u,deep[u],d+e[i].w);
            size[u]+=size[v];
        }
    }
}

void getmx(int u,int pre){
    gmax(tmp[deep[u]],dis[u]);
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(!Del[v]&&v!=pre)  getmx(v,u);
    }
}

bool cmp(const node &u,const node &v){
    if(u.first!=v.first)    return u.first<v.first;
    return u.second<v.second;
}

void work(int u){
    Del[u]=true;
    int cnt=0;
    K-=Color[u];
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(!Del[v]){
            deep_mx=0;
            getdeep(v,u,0,e[i].w);
            st[cnt++]=(node){deep_mx,v};
        }
    }
    sort(st,st+cnt,cmp);
    for(int i=0;i<cnt;i++){
        getmx(st[i].second,u);
        int now=0;
        if(i!=0)
             for(int j=st[i].first;j>=0;j--){
                 while(now+j<K&&now<st[i-1].first)
                     ++now,gmax(mx[now],mx[now-1]);
                 if(now+j<=K)   gmax(ans,mx[now]+tmp[j]);
             }
        if(i!=cnt-1)
             for(int j=st[i].first;j>=0;j--)
                  gmax(mx[j],tmp[j]),tmp[j]=0;
        else
            for(int j=st[i].first;j>=0;j--){
                  gmax(mx[j],tmp[j]);
                  if(j<=K)  gmax(ans,mx[j]);
                  tmp[j]=mx[j]=0;
             }
    }
    K+=Color[u];
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(!Del[v]){
            Count=f[0]=size[v];
            getroot(v,root=0);
            work(root);
        }
    }
}

int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}


int main(){
    int n,m;
    n=read(),K=read(),m=read();
    tot=0;
    memset(head,-1,sizeof(head));
    int u,v,w;
    for(int i=1;i<=m;i++)
        u=read(),Color[u]=1;
    for(int i=1;i<n;i++){
        u=read(),v=read(),w=read();
        addedge(u,v,w),addedge(v,u,w);
    }
    Count=f[0]=n;
    getroot(1,root=0);
    ans=0;
    work(root);
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值