spoj 1825 Free tour II

http://www.spoj.com/problems/FTOUR2/

After the success of 2nd anniversary (take a look at problem FTOUR for more details), this 3rd year, Travel Agent SPOJ goes on with another discount tour.

The tour will be held on ICPC island, a miraculous one on the Pacific Ocean. We list N places (indexed from 1 to N) where the visitors can have a trip. Each road connecting them has an interest value, and this value can be negative(if there is nothing interesting to view there). Simply, these N places along with the roads connecting them form atree structure. We will choose two places as the departure and destination of the tour.

Since September is the festival season of local inhabitants, some places are extremely crowded (we call themcrowded places). Therefore, the organizer of the excursion hopes the tour will visit at most K crowded places (too tiring to visit many of them) and of course, the total number of interesting value should be maximum.

Briefly, you are given a map of N places, an integer K, and M id numbers of crowded place. Please help us to find the optimal tour. Note that we can visit each place only once (or our customers easily feel bored), also the departure and destination places don't need to be different.

Input

There is exactly one case. First one line, containing 3 integers N K M, with 1 <= N <= 200000, 0 <= K <= M, 0 <= M <=N.

Next M lines, each line includes an id number of a crowded place.

The last (N - 1) lines describe (N - 1) two-way roads connected N places, form a b i, with a, b is the id of 2 places, and i is its interest value (-10000 <= i <= 10000).

Output

Only one number, the maximum total interest value we can obtain.

Example

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

题意:n个点的一棵树,有m个黑点,问最多经过k个黑点的最长路径
点分治+启发式合并
对于每一个次的根节点,枚举计算每个孩子的子树中最多有几个黑点
注意当黑点数>k时,及时return k
按黑点数从小到大排序,
对于每个子树
计算子树中经过i个黑点的最长路径dis[i]
同时维护一个数组f,表示前i-1个孩子的各自子树内,经过黑点数<=j的最长路径
枚举这个子树经过多少个黑点,更新答案
有可能最长路就是子树内一个点与根节点之间,所以在计算dis[i]时,也要更新答案

具体看漆子超神犇的论文

为什么时间复杂度是nlogn呐?

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 200002
using namespace std;
int n,k,m,root,ans,max_black,all;
bool crowd[N],vis[N];
int tot,front[N],nxt[N<<1],to[N<<1],val[N<<1];
int siz[N],big[N];
int dis[N],f[N];
struct node
{
    int black,id,d;
}e[N];
void read(int &x)
{
    x=0; int f=1; char c=getchar();
    while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    x*=f;
}
void add(int u,int v,int w)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; val[tot]=w;
}
void getroot(int x,int fa)
{
    siz[x]=1; big[x]=0;
    for(int i=front[x];i;i=nxt[i])
    {
        if(vis[to[i]] || to[i]==fa) continue;
        getroot(to[i],x);
        siz[x]+=siz[to[i]];
        big[x]=max(big[x],siz[to[i]]);
    }
    big[x]=max(big[x],all-siz[x]);
    if(big[x]<big[root]) root=x;
}
int cal_black(int x,int fa,int sum)
{
    if(sum==k) return k;
    int res=sum;
    for(int i=front[x];i;i=nxt[i])
    {
        if(vis[to[i]] || to[i]==fa) continue;
        res=max(res,cal_black(to[i],x,sum+crowd[to[i]]));
    }
    return res;
}
bool cmp(node p,node q)
{
    return p.black<q.black;
}
void cal_dis(int x,int fa,int sum,int d)
{
    if(sum<=k)
    {
        ans=max(ans,d);
        dis[sum]=max(dis[sum],d);
    }
    for(int i=front[x];i;i=nxt[i])
    {
        if(to[i]==fa || vis[to[i]]) continue;
        cal_dis(to[i],x,sum+crowd[to[i]],d+val[i]);
    }
}
void cal(int x)
{
    tot=max_black=0;
    for(int i=front[x];i;i=nxt[i]) 
    {
        if(vis[to[i]]) continue;
        e[++tot].id=to[i];
        e[tot].black=cal_black(to[i],x,crowd[to[i]]);
        e[tot].d=val[i];
        max_black=max(max_black,e[tot].black);
    }
    sort(e+1,e+tot+1,cmp);
    memset(f,-127,sizeof(*f)*(max_black+1));
    for(int i=1;i<=tot;i++)
    {
        memset(dis,-127,sizeof(*dis)*(e[i].black+1));
        cal_dis(e[i].id,x,crowd[e[i].id],e[i].d);
        for(int j=crowd[e[i].id];j<=min(k-crowd[x],e[i].black);j++)
            ans=max(ans,f[min(k-j-crowd[x],e[i-1].black)]+dis[j]);
        f[0]=max(f[0],dis[0]);
        for(int j=1;j<=e[i].black;j++) 
        f[j]=max(f[j],dis[j]),f[j]=max(f[j-1],f[j]);
    }
}
void work(int x)
{
    cal(x);
    vis[x]=true;
    for(int i=front[x];i;i=nxt[i])
    {
        if(vis[to[i]]) continue;
        all=siz[to[i]];
        root=0;
        getroot(to[i],0);
        work(root);
    }
}
int main()
{
    read(n); read(k); read(m);
    int x;
    while(m--)
    {
        read(x);
        crowd[x]=true;
    }
    int u,v,w;
    for(int i=1;i<n;i++)
    {
        read(u); read(v); read(w);
        add(u,v,w);
    }
    big[root]=n;
    root=0;
    all=n;
    getroot(1,0);
    work(root);
    printf("%d",ans);
}  
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7504930.html