jzoj3329 【NOI2013模拟】树上的路径 (点分治,前K大问题)

题意

给定一棵N个结点的树,结点用正整数1..N编号,每条边有一个正整数权值。用d(a,b)表示从结点a到结点b路径上经过边的权值和,其中要求 a < b 。将这N*(N-1)/2个距离值从大到小排序,输出前M个距离值。

对于100%的数据满足N<=50,000,M<=min{N*(N-1)/2,300,000}。

+O2

题解

有两种解法。
第一种非常经典,二分第M大在哪,然后去统计个数。
统计个数也很简单,裸的点分治就行。
但是这样二分 + 排序 + 点分治 是三个log,妥妥的TLE
其实可以先排好序提出来,后面不需要再进点分治和排序。这样复杂度可以减掉一个log
用vector比较方便。

第二种解法也是经典套路
求第K大,那么我们发现,对于一个确定的分治重心,每个点与他能成被统计路径的点 是之前的子树。这样我们把所谓的点分治序列建出来,就相当于在这几个子树区间选最大的放进堆里,取出后再分为两个最大值候选区间放进堆里。这样从堆中取m次。
然后最大值用倍增表(不用好像也没啥问题)

这样是两个log的,但理论复杂度会比第一种慢(log (n log n)),但是常数小了很多,实际要快。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 5e4 + 10;
int n,m;
int final[N],nex[2 * N],to[2 * N],tot,e[2 * N],size[N];
int stm,w[16 * N],L,R;
int vis[N],allsz,hvy,hmx;

struct node{
    int l,r,x,mx;
    friend bool operator < (const node &a, const node &b) {
        return w[a.x] + w[a.mx] < w[b.x] + w[b.mx];
    }
};

priority_queue<node> que;

void link(int x,int y,int c) {
    to[++tot] = y;
    nex[tot] = final[x];
    final[x] = tot;
    e[tot] = c;
}
void init(int x,int fa) {
    size[x] = 1;
    for (int i=final[x]; i; i=nex[i]) { 
        int y = to[i];
        if (y != fa && !vis[y]) {
            init(y,x);
            size[x] += size[y];
        }
    }
}

void gethvy(int x,int fa) {
    int mx = 0;
    for (int i=final[x]; i; i=nex[i]) { 
        int y = to[i];
        if (y != fa && !vis[y]) {
            gethvy(y,x);
            mx = max(mx, size[y]);
        }
    }
    mx = max(mx,allsz - size[x]);
    if (hvy == 0 || hmx > mx) hvy = x, hmx = mx;
}

int pre,gre;
void dfs(int x,int fa,int dis) {
    w[++stm] = dis;
    que.push((node){L,R,stm,pre});
    if (w[gre] < w[stm]) gre = stm;

    for (int i=final[x]; i; i=nex[i]) {
        int y = to[i];
        if (y != fa && !vis[y]) {
            dfs(y,x,dis+e[i]);
        }
    }
}

void solve(int root) {
    hvy = 0;
    init(root,0), allsz = size[root];
    gethvy(root,0);

    w[L = R = ++stm] = 0;
    pre = gre = L;
    for (int i=final[hvy]; i; i=nex[i]) {
        int y = to[i];
        if (!vis[y]) {
            dfs(y, hvy, e[i]);
            R = stm;
            pre = gre;
        }
    }

    vis[hvy] = 1;
    for (int i=final[hvy]; i; i=nex[i]) {
        int y = to[i]; if (!vis[y]) {
            solve(y);
        }
    }
}

int g[16 * N][20];
int mi[20],log[16 * N];
node top;

int fmax(int l,int r) {
    int sz = r - l + 1;
    int z = log[sz] - 1;
    return w[g[l][z]] > w[g[r - (1<<z) + 1][z]] ? g[l][z] : g[r - (1<<z) + 1][z];
}

int main() {
    freopen("tp.in","r",stdin);
    cin>>n>>m;
    for (int i=0; i<=20; i++) mi[i] = 1<<i;
    for (int i=1; i<n; i++) {
        int a,b,c;scanf("%d %d %d",&a,&b,&c);
        link(a,b,c), link(b,a,c);
    }
    solve(1);
    for (int i=1; i<=stm; i++) log[i] = log[i>>1] + 1;
    for (int i=stm; i; i--) {
        g[i][0] = i;
        for (int j=1; j<20; j++) {
            int nx = i + mi[j-1];
            if (nx <= stm)
                g[i][j] = (w[g[i][j-1]] > w[g[nx][j-1]]) ? g[i][j-1] : g[nx][j-1];
            else
                g[i][j] = g[i][j-1];
        }
    }
    for (int z=1; z<=m; z++) {
        top = que.top(); que.pop();
        printf("%d\n",w[top.x] + w[top.mx]);
        if (top.l < top.mx)
            que.push((node) {top.l,top.mx-1,top.x,fmax(top.l,top.mx-1) });
        if (top.mx < top.r)
            que.push((node) {top.mx+1,top.r,top.x,fmax(top.mx+1,top.r) });
    }
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/81005871
今日推荐