BZOJ3784:树上的路径

浅谈树分治:https://www.cnblogs.com/AKMer/p/10014803.html

题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3784

难得地看了题解,发现居然还有点分序这么个玩意儿……

对于点分治时遍历过的点的长度为\(nlogn\)的序列,我们称它为点分序。

然后这题就是树上超级钢琴,在点分序上做就行了。对于每一条边,在点分序里能与其匹配的边是一段区间。

时间复杂度:\(O(nlogn*(1+log(nlogn)))\)

空间复杂度:\(O(nlogn*log(nlogn))\)

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
 
const int maxn=5e4+5;
 
bool vis[maxn];
int n,m,mx,rt,N,tot,cnt,L,R;
int now[maxn],pre[maxn*2],son[maxn*2],val[maxn*2];
int siz[maxn],a[maxn*20],Log[maxn*20],f[20][maxn*20];
 
int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
 
void add(int a,int b,int c) {
    pre[++tot]=now[a];
    now[a]=tot,son[tot]=b,val[tot]=c;
}
 
struct node {
    int v,pos,l,r;
 
    node() {}
 
    node(int _v,int _pos,int _l,int _r) {
        v=_v,pos=_pos,l=_l,r=_r;
    }
 
    bool operator<(const node &a)const {
        return v<a.v;
    }
};
 
struct Heap {
    int tot;
    node tree[maxn*26];
 
    void ins(node res) {
        tree[++tot]=res;
        int pos=tot;
        while(pos>1) {
            if(tree[pos>>1]<tree[pos])
                swap(tree[pos>>1],tree[pos]),pos>>=1;
            else break;
        }
    }
 
    node pop() {
        node res=tree[1];
        tree[1]=tree[tot--];
        int pos=1,son=2;
        while(son<=tot) {
            if(son<tot&&tree[son]<tree[son|1])son|=1;
            if(tree[pos]<tree[son])
                swap(tree[son],tree[pos]),pos=son,son=pos<<1;
            else break;
        }
        return res;
    }
}T;
 
void find_rt(int fa,int u) {
    int res=0;siz[u]=1;
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v]&&v!=fa)find_rt(u,v),siz[u]+=siz[v],res=max(res,siz[v]);
    res=max(res,N-siz[u]);
    if(res<mx)mx=res,rt=u;
}
 
void solve(int fa,int u,int dis) {
    a[++cnt]=dis;T.ins(node(a[mx]+dis,cnt,L,R)),siz[u]=1;
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v]&&v!=fa)solve(u,v,dis+val[p]),siz[u]+=siz[v];
}
 
void work(int u,int size) {
    N=size,mx=rt=n+1,find_rt(0,u);
    u=rt,vis[u]=1,a[++cnt]=0,L=cnt,mx=cnt;
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v]) {
            R=cnt,solve(u,v,val[p]);
            for(int j=R+1;j<=cnt;j++)
                if(a[j]>a[mx])mx=j;
        }
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v])work(v,siz[v]);
}
 
int fake(int num1,int num2) {
    if(a[num1]>a[num2])return num1;
    return num2;
}
 
int query(int l,int r) {
    int x=Log[r-l+1];
    return fake(f[x][l],f[x][r-(1<<x)+1]);
}
 
void make_st() {
    Log[0]=-1;
    for(int i=1;i<=cnt;i++)
        f[0][i]=i,Log[i]=Log[i>>1]+1;
    for(int i=1;i<=19;i++)
        for(int j=1;j+(1<<i)-1<=cnt;j++)
            f[i][j]=fake(f[i-1][j],f[i-1][j+(1<<(i-1))]);
}
 
int main() {
    n=read(),m=read();
    for(int i=1;i<n;i++) {
        int a=read(),b=read(),c=read();
        add(a,b,c),add(b,a,c);
    }work(1,n),make_st();
    for(int i=1;i<=m;i++) {
        node tmp=T.pop();
        int pos=query(tmp.l,tmp.r);
        if(pos-1>=tmp.l)
            T.ins(node(a[query(tmp.l,pos-1)]+a[tmp.pos],tmp.pos,tmp.l,pos-1));
        if(pos+1<=tmp.r)
            T.ins(node(a[query(pos+1,tmp.r)]+a[tmp.pos],tmp.pos,pos+1,tmp.r));
        printf("%d\n",tmp.v);
    }
    return 0;
}
posted @ 2018-12-16 21:41  AKMer  阅读(207)  评论(5编辑  收藏  举报