UVA 12161 Ironman Race in Treeland (树分治)

题意:求树上的一条费用不超过m的路径,使得总长度尽量大。

人参第一发树分治,紫书上思路讲得比较清晰,这里不再赘述。

实现的时候,用一个类似时间戟的东西,记录结点首次访问的时间,并保存结点序列。

合并的时候用map组织有序表。和Defense Lines类似

复杂度O(nlog^2n)

#include<bits/stdc++.h>
using namespace std;


const int maxn = 3e4+5;
int n,m;
int hd[maxn], nx[maxn<<1], to[maxn<<1], dam[maxn<<1], len[maxn<<1], ec;

inline void add(int u,int v,int D,int L)
{
    to[ec] = v;
    dam[ec] = D;
    len[ec] = L;
    nx[ec] = hd[u];
    hd[u] = ec++;
}

int root, best;
int ct[maxn];
//best = n
void GetBaryCenter(int u,int f)
{
    ct[u] = 1;
    int Mx = 0;
    for(int i = hd[u]; ~i; i = nx[i]){
        if(to[i] != f){
            GetBaryCenter(to[i], u);
            ct[u] += ct[to[i]];
            Mx = max(Mx,ct[to[i]]);
        }
    }
    Mx = max(Mx,n-ct[u]);
    if(Mx < best){
        best = Mx;
        root = u;
    }
}

#define MP make_pair
#define fi first
#define se second
int C[maxn], L[maxn];

map<int,int> mp;
map<int,int>::iterator it;

int ans;
int path[maxn];
int pre[maxn], dfs_clk;
//dfs_clk = 0, ans = 0;
void dfs(int u = root,int f = 0)
{
    path[dfs_clk] = u;
    pre[u] = dfs_clk++;
    C[u] = 0; L[u] = 0;
    for(int i = hd[u]; ~i; i = nx[i]){
        if(to[i] != f){
            int v = to[i];
            dfs(v,u);
            for(int j = pre[v]; j < dfs_clk ; j++){
                int x = path[j];
                C[x] += dam[i];
                L[x] += len[i];
            }
        }
    }
    //子树都访问完里以后统一处理以保证互不干扰,这样mp就可以设置为全局变量
    mp.clear();
    for(int i = hd[u]; ~i; i = nx[i]){
        int nex = nx[i];
        int lim = ~nex? pre[to[nex]] : dfs_clk;
        if(to[i] != f){
            int v = to[i];
            for(int j = pre[v]; j < lim ; j++){
                int x = path[j];
                if(C[x] <= m){
                    ans = max(ans,L[x]);
                    it = mp.upper_bound(m-C[x]);//找到一个key <= C[x]
                    if(it != mp.begin()){
                        ans = max(ans,(--it)->second+L[x]);
                    }
                }
            }
            for(int j = pre[v]; j < lim ; j++){
                int x = path[j];
                if(C[x] <= m){
                    it = mp.lower_bound(C[x]);
                    bool swc = false;
                    if(it == mp.begin() || (swc = true , L[x] > (--it)->se) ){
                        if(swc) it++;
                        while(it != mp.end() && it->se <= L[x]) mp.erase(it++);
                        if(it == mp.end() || it->fi > C[x]) {//lower_bound可能使得it指向key == C[x] 而 val >= L[x]
                            mp.insert(MP(C[x],L[x]));
                        }
                    }
                }
            }
        }
    }
}

//#define LOCAL
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif
    int T, ks = 0; cin>>T;
    while(T--){
        scanf("%d%d",&n,&m);
        memset(hd+1,-1,sizeof(int)*n); ec = 0;
        for(int i = n; --i;){
            int a,b,D,L; scanf("%d%d%d%d",&a,&b,&D,&L);
            add(a,b,D,L); add(b,a,D,L);
        }
        best = n+1;
        GetBaryCenter(1,0);
        dfs_clk = ans = 0;
        dfs();
        printf("Case %d: %d\n",++ks,ans);
    }
    return 0;
}

 

posted @ 2015-10-07 13:46  陈瑞宇  阅读(511)  评论(1编辑  收藏  举报