BZOJ 3784: 树上的路径

Description

问一棵树上前 \(k\) 大路径的边权.

Sol

边分治.

非常感谢数据没有菊花图.

为了写写边分治试试然后就开了这道题.

边分治非常好想,选一条重边,分成两部分,然后分别求最大值,对每个重边建一个堆维护一下,全局堆里存答案.

rebuild好像写的有问题啊qwq...疯狂RE不止...最后不管了,直接不重建树也能A...

rebuild我就是想的将他建成二叉树的样子,每个点的度数不超过3或4差不多就可以了.

Code

/**************************************************************
    Problem: 3784
    User: BeiYu
    Language: C++
    Result: Accepted
    Time:4540 ms
    Memory:26660 kb
****************************************************************/

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

#define debug(a) cout<<#a<<"="<<a<<" "
#define mpr make_pair
typedef pair< int,int > pr;
typedef pair< int,pr > prr;
const int N = 100500;

int n,k,rt,rtt;
int d[N],sz[N],ud[N];
int vis[N<<1];

int cnte;
struct Edge { int to,w; }edge[N<<2];
vector< int > g[N];
priority_queue< pr > qq;
priority_queue< pr > q[N];
vector< int > nxt[N];
vector< int > tmp[N][2];

inline int in(int x=0,char ch=getchar()) { while(ch>‘9‘ || ch<‘0‘) ch=getchar();
    while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();return x; }

void AddEdge(int fr,int to,int w) { g[fr].push_back(cnte);edge[cnte++]=(Edge){ to,w }; }

void Print() {
    cout<<"start"<<endl;
    for(int i=1;i<=n;i++) {
        cout<<i<<"-->";
        for(int j=0;j<(int)g[i].size();j++) cout<<"id:"<<g[i][j]<<" "<<edge[g[i][j]].to<<" ";
        cout<<endl;
    }
    cout<<"-------------------------"<<endl;
}

void ReBuild(int u,int fa) {
    int cnt=0,id=-1,lst=0,fst=0;
    if(g[u].size()>3) for(int i=g[u].size()-1,v;i>=1;i--)
        if((v=edge[g[u][i]].to)!=fa) {
            if(cnt&1) {
                g[n].push_back(g[u][i]);
            }else {
                g[++n].push_back(g[u][i]);
                if(lst) {
                    AddEdge(lst,n,0);
                    AddEdge(n,lst,0);
                }else {
                    fst=n;
                }lst=n;
            }
            cnt^=1;
            edge[g[u][i]^1].to=n;
            g[u].pop_back();
        }else id=g[u][i],g[u].pop_back();
    if(id!=-1) g[u].push_back(id);
    if(fst) AddEdge(u,fst,0),AddEdge(fst,u,0);
    for(int i=0;i<(int)g[u].size();i++) if(edge[g[u][i]].to!=fa) ReBuild(edge[g[u][i]].to,u);

}

void GetSize(int u,int fa) {
    d[u]=d[fa]+1,sz[u]=1;
    for(int i=g[u].size()-1,v;~i;i--) if((v=edge[g[u][i]].to)!=fa && !vis[g[u][i]]) {
        GetSize(v,u),sz[u]+=sz[v];
    }
}
void GetRoot(int u,int nn) {
    rt=-1,rtt=N;GetSize(u,u);
//  for(int i=1;i<=n;i++) cout<<sz[i]<<" ";cout<<endl;
//  for(int i=1;i<=n;i++) cout<<d[i]<<" ";cout<<endl;
    queue< pr > q;q.push(mpr(u,u));
    for(pr x;!q.empty();) {
        x=q.front(),q.pop();
        int u=x.first,tmp;
        for(int i=0,v;i<(int)g[u].size();i++) if((v=edge[g[u][i]].to)!=x.second && !vis[g[u][i]]){
            if(d[u]>d[v]) tmp=max(sz[u],nn-sz[u]);
            else tmp=max(sz[v],nn-sz[v]);
//          debug(u),debug(v),debug(tmp),debug(g[u][i])<<endl;
            if(tmp<rtt) rtt=tmp,rt=g[u][i];
            q.push(mpr(v,u));
        }
    }
//  debug(u),debug(rt)<<endl;
}
void GetDep(int u,int fa,int w,vector< int > &vv) {
    vv.push_back(w);
    for(int i=0,v;i<(int)g[u].size();i++) if((v=edge[g[u][i]].to)!=fa && !vis[g[u][i]]) {
        GetDep(v,u,w+edge[g[u][i]].w,vv);
    }
}

void GetAns(int x,int nn) {
    vis[x]=vis[x^1]=1;
    int u=edge[x].to,v=edge[x^1].to;
    GetDep(u,u,0,tmp[x][0]),GetDep(v,v,0,tmp[x][1]);

    sort(tmp[x][0].begin(),tmp[x][0].end(),greater< int >());
    sort(tmp[x][1].begin(),tmp[x][1].end(),greater< int >());

//  cout<<"---------------------"<<endl;
//  debug(x),debug(nn),debug(u),debug(v)<<endl;
//  for(int i=0;i<(int)tmp[x][0].size();i++) cout<<tmp[x][0][i]<<" ";cout<<endl;
//  for(int i=0;i<(int)tmp[x][1].size();i++) cout<<tmp[x][1][i]<<" ";cout<<endl;

    nxt[x].resize(tmp[x][0].size());
    for(int i=0;i<(int)tmp[x][0].size();i++) nxt[x][i]=0;

    for(int i=0;i<(int)tmp[x][0].size();i++) q[x].push(mpr(tmp[x][0][i]+tmp[x][1][0]+edge[x].w,i));

//  debug(tmp[x][0][i]+tmp[x][1][0]+edge[x].w)
//  debug(q[x].top().first)<<endl;
    qq.push(mpr(q[x].top().first,x));

    if(d[u]<d[v]) swap(u,v);
    int ss=sz[u];
    GetRoot(u,ss);
    if(rt!=-1) GetAns(rt,ss);
    GetRoot(v,nn-ss);
    if(rt!=-1) GetAns(rt,nn-ss);
}
void init() {
    n=in(),k=in();
    for(int i=1,u,v,w;i<n;i++)
        u=in(),v=in(),w=in(),AddEdge(u,v,w),AddEdge(v,u,w);

//  ReBuild(1,1);

//  Print();

//  cout<<"qwq"<<endl;

    GetRoot(1,n);

//  debug(rt)<<endl;

    GetAns(rt,n);

}
void Del(int x) {
    pr r=q[x].top();
    q[x].pop();
    int u=r.second;
    if(nxt[x][u]+1<(int)tmp[x][1].size()) nxt[x][u]++,q[x].push(mpr(tmp[x][0][u]+tmp[x][1][nxt[x][u]]+edge[x].w,u));
    qq.push(mpr(q[x].top().first,x));
}
int Query() {
    pr x=qq.top();qq.pop();
    int r=x.first,u=x.second;
    Del(u);

    return r;
}
int main() {
//  freopen("in.in","r",stdin);
//  ios::sync_with_stdio(false);
    init();

    for(;k--;) {
        printf("%d\n",Query());
    }

    return 0;
}

  

时间: 2024-08-28 13:29:02

BZOJ 3784: 树上的路径的相关文章

bzoj 3784: 树上的路径 堆维护第k大

3784: 树上的路径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 88  Solved: 27[Submit][Status][Discuss] Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值. Input 第一行两个正整数N,M 下面N-1行,每行三个正整数a,b,

【BZOJ-3784】树上的路径 点分治 + ST + 堆

3784: 树上的路径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 462  Solved: 153[Submit][Status][Discuss] Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值. Input 第一行两个正整数N,M 下面N-1行,每行三个正整数a,

【BZOJ3784】树上的路径 点分治序+ST表

[BZOJ3784]树上的路径 Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值. Input 第一行两个正整数N,M 下面N-1行,每行三个正整数a,b,c(a,b<=N,C<=10000).表示结点a到结点b有一条权值为c的边. Output 共M行,如题所述. Sample Input 5 10 1 2 1

BZOJ - 3757 树上莫队解决离线路径问题 &amp; 学习心得

题意:给你一棵树,求u,v最短路径的XXX(本题是统计权值种类) 今天课上摸鱼学了一种有意思的处理路径方式(其实是链式块状树翻车了看别的),据说实际运行跑的比XX记者还快 大概就是像序列莫队那样 首先是对暴力查询的优化 第一关键字是块(树上分块),第二关键字是dfs序,这样保证了离线操作的下界最优 其次是转移的优化 我把大佬的话再转述一遍: 设\(S(u,v)\):\(u-v\)最短路径所覆盖的点集 \(S(u,v)=S(root,u)⊕S(root,v)⊕lca(u,v)\) 记\(T(u,v

SPOJ COT2 树上找路径上不同值的个数

题目大意 给出多个询问u , v , 求出u-v路径上点权值不同的个数 开始做的是COT1,用主席树写过了,理解起来不难 很高兴的跑去做第二道,完全跟普通数组区间求k个不同有很大区别,完全没思路 膜拜http://www.cnblogs.com/oyking/p/4265823.html 这里利用莫队思想来做,在树上分块,尽可能让相连部分作为一个联通块,那么就在dfs过程中加个手写栈,如果回溯上来的时候保存的值的个数超过每块中应有的 个数那么就将他们分到同一个id块 排序也是跟普通莫队上一样,按

bzoj 3757 树上莫队

感谢以下文章作者: http://blog.csdn.net/kuribohg/article/details/41458639 http://vfleaking.blog.163.com/blog/static/174807634201311011201627/ http://blog.csdn.net/jiangyuze831/article/details/41476865 http://hzwer.com/5259.html 做了树上的莫队,感觉对这个算法的思想理解更深了 先分块,不论怎

BZOJ 3251 树上三角形 暴力

题目大意:给定一棵树,每个点上有点权,多次修改点权,以及查询两点间路径上所有点权之间能否找出三个值构成三角形的三边长 被逗了- - 首先考虑如果一些数不能构成三角形的三边长,那么这些数最多有多少个? 显然当这些数构成斐波那契数列的时候数值的个数最多- - 那么2^31以内共有多少个斐波那契数?46! 也就是说当两点间路径上的点>=47时答案一定是YES! 那么小于47时只要暴力就行- - 时间复杂度O(mlogk) 其中k是最大的数的大小- - #include <cstdio> #in

BZOJ3784 : 树上的路径

树的点分治,在分治的时候将所有点到根的距离依次放入一个数组q中. 对于一棵子树里的点,合法的路径一定是q[L]..q[R]的某个数加上自己到重心的距离. 定义五元组(v,l,m,r,w),表示当前路径长度为v,在[l,r]里选出最大值m,并加上w. 用大根堆维护这些五元组,每次取出v最大的元素,并扩展出[l,m-1]以及[m+1,r]两个状态,用线段树查询区间最大值. 时间复杂度$O(n\log^2n+m\log n)$. #include<cstdio> #include<queue&

BZOJ 4034 树上操作(树的欧拉序列+线段树)

刷个清新的数据结构题爽一爽? 题意: 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 注意到操作3,询问x到根的路径之间点权和,容易发现这就是欧拉序列中的前缀和. 所以按照树的欧拉序列建线段树,然后操作1就变成单点修改,操作2,就变成了区间内某些点+a,某些点-a,也容易用tag标记