SPOJ FTOUR2 Free tour II (点分治+启发式合并)

题意:

树上n个点,点有黑有白,一条路径上黑点个数不超过k个的最大权值是多少

思路:

     从点分治上想,我们每次统计通过某个点的,符合条件的路径最大权值,然后不停更新答案。当我们遍历完某个子树,就把这个子树的信息和之前子树合并,然后不停更新即可。很明显,我们要维护当经过黑点为x个时的最大权值。
    假设我们在遍历某棵子树的时候,得到了当黑点个数为x个的最大权值,那么在之前已经遍历过的子树中,0~k-x(如果根为黑则还要减一)个黑点的权值都是可行的,这里可以用线段树暴力维护,这样复杂度为 O ( n ( log 2 n ) 2 ) ,这样很暴力,也很好想,然而会TLE。
    考虑如何 O ( n log 2 n ) 的方法,首先我们发现,我们维护的最大权值,其实可以等价为一个非严格递增的数组(因为1个黑点的答案可以,那么我0个黑点的也自然可以),如果我们暴力地已知遍历数组维护,最差达到 n 2 (比如:我第一个子树大小为n/2,后面子树每个大小为1,那么一共就要遍历 n 2 4 次),考虑如何减少这个次数,我们发现,子树遍历顺序不影响最终答案,那么我们可以处理出子树最大黑点个数,利用这个排序,这样就能 O ( n ) 地暴力更新那个数组了(这个东西就是启发式合并。。。)

错误及反思:

     把j写成now,因为这个找了一天bug,整个人都不好了。。。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 200005;
struct E{
    int to,next;
    int val;
}e[N*2];

struct D{
    int to,val,deep;
};
bool cmp(D a,D b){return a.deep<b.deep;}

int first[N],n,k,m,tot=0,si[N],max_si[N],ans=0,f[N],tmp[N];
bool did[N],black[N];
vector<D> v;

void addedge(int x,int y,int z){
    e[tot].to=y; e[tot].val=z; e[tot].next=first[x]; first[x]=tot++;
    e[tot].to=x; e[tot].val=z; e[tot].next=first[y]; first[y]=tot++;
}

void dfs_size(int now,int fa){
    si[now]=1;
    max_si[now]=0;
    for(int i=first[now];i!=-1;i=e[i].next)
        if(e[i].to!=fa&&!did[e[i].to]){
            dfs_size(e[i].to,now);
            si[now]+=si[e[i].to];
            max_si[now]=max(max_si[now],si[e[i].to]);
        }
}

void dfs_root(int now,int fa,int& root,int& maxsize,int t){
    int tmax=max(max_si[now],si[t]-si[now]);
    if(tmax<maxsize){
        maxsize=tmax;
        root=now;
    }
    for(int i=first[now];i!=-1;i=e[i].next)
        if(e[i].to!=fa&&!did[e[i].to])
            dfs_root(e[i].to,now,root,maxsize,t);
}

void dfs_subtree(int now,int fa,int num,int val){
    for(int i=first[now];i!=-1;i=e[i].next)
        if(e[i].to!=fa&&!did[e[i].to]){
            if(black[e[i].to]) dfs_subtree(e[i].to,now,num+1,val+e[i].val);
            else dfs_subtree(e[i].to,now,num,val+e[i].val);
        }
    tmp[num]=max(tmp[num],val);
}

void dfs_deep(int now,int fa,int dep,int &maxn){
    if(dep>k) return ;
    maxn=max(maxn,dep);
    for(int i=first[now];i!=-1;i=e[i].next)
        if(e[i].to!=fa&&!did[e[i].to]){
            if(black[e[i].to]) dfs_deep(e[i].to,now,dep+1,maxn);
            else dfs_deep(e[i].to,now,dep,maxn);
        }
}

void solve(int now){
    int root,maxsize=1e9,td=0;
    v.clear();
    dfs_size(now,-1);
    dfs_root(now,-1,root,maxsize,now);
    did[root]=true;
    if(black[root]) td++;

    for(int i=first[root];i!=-1;i=e[i].next){
        if(!did[e[i].to]){
            int max_deep=0;
            if(black[e[i].to]) dfs_deep(e[i].to,root,1,max_deep);
            else dfs_deep(e[i].to,root,0,max_deep);
            v.push_back({e[i].to,e[i].val,max_deep});
        }
    }
    sort(v.begin(),v.end(),cmp);

    for(int i=0;i<v.size();i++){
        if(black[v[i].to]) dfs_subtree(v[i].to,root,1,v[i].val);
        else dfs_subtree(v[i].to,root,0,v[i].val);
        int now=0;
        if(i){
            for(int j=v[i].deep;j>=0;j--){
                while(now+j+td<k&&now<v[i-1].deep)
                    now++;
                if(now+j+td<=k)
                    ans=max(ans,f[now]+tmp[j]);
            }
        }
        for(int j=0;j<=v[i].deep;j++){
            f[j]=max(f[j],tmp[j]);
            tmp[j]=0;
        }
        for(int j=1;j<=v[i-1].deep;j++)
            f[j]=max(f[j],f[j-1]);

    }
    if(v.size()){
        for(int j=0;j<=v[v.size()-1].deep;j++){
            if(j+td<=k) ans=max(ans,max(tmp[j],f[j]));
            tmp[j]=f[j]=0;
        }
    }
    for(int i=first[root];i!=-1;i=e[i].next)
        if(!did[e[i].to])
            solve(e[i].to);
}

int main(){
    memset(first,-1,sizeof(first));
    scanf("%d%d%d",&n,&k,&m);
    for(int i=0,kk;i<m;i++){
        scanf("%d",&kk);
        black[kk]=true;
    }
    for(int i=0,u,v,w;i<n-1;i++){
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
    }
    solve(1);
    printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/roll_keyboard/article/details/80718259