zoukankan      html  css  js  c++  java
  • 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);
    }  
  • 相关阅读:
    html禁止手机页面放大缩小
    <httpProtocol/>配置http协议头
    C# 并行编程 之 并发集合 (.Net Framework 4.0)(转)
    JavaScript随机排序算法1
    用户消息处理方式
    后台单用户在线,简单处理
    使用 Intel HAXM 为 Android 模拟器加速,媲美真机(转)
    解决Android SDK Manager下载太慢问题(转)
    如何正确并完全安装Visual Studio 2015企业版本?(转)
    C# XML流操作简单实例
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7504930.html
Copyright © 2011-2022 走看看