bzoj5129: [Lydsy1712月赛]树上传送

首先每个点可以更新的点的最短路都是 d i s [ i ] + c o s [ i ] dis[i]+cos[i] dis[i]+cos[i],所以维护一个堆,按排序,这样每个节点只会被修改一次

那么如何快速找出所有没有更新的点呢,我们考虑点分树

从每个重心开始bfs,然后用队列记录下所遍历到的每个点,这显然总共只有 n l o g n nlogn nlogn个节点

查找没修改过的点,对于子树内的点,可以直接删队列内的点,对于子树外的点,可以找点分树父亲的队列进行修改

每次最多爬 l o g log log的父亲, 总共只会删 n l o g n nlogn nlogn个节点

复杂度 O ( n l o g n ) O(nlogn) O(nlogn)


开系统队列直接炸内存了,只好手写了一个

#include 
#include 
#include 
using namespace std;
 
#define dd c=getchar()
int read() {int s=0,w=1;char c;while (dd,c>'9' || c<'0') if (c=='-') w=-1;while (c>='0' && c<='9') s=s*10+c-'0',dd;return s*w;}
#undef dd
void write(int x) {if (x<0) putchar('-'),x=-x;if (x>=10) write(x/10);putchar(x%10|'0');}
void wln(int x) {write(x);putchar('\n');}void wsp(int x) {write(x);putchar(' ');}
 
#define ll long long
const int N = 3e5+7;
struct edge {
    int t,nxt;
}e[N<<1];
int n,S,cnt,T;
int head[N],lim[N],vis[N],dfn[N],pos[N],dep[N],lg[N<<1],mn[N<<1][20];
ll dis[N],cos[N];
 
struct cmp {
    bool operator () (int a, int b) const {
        return dis[a] + cos[a] > dis[b] + cos[b];
    }
};
priority_queue<int,vector<int>, cmp> q;
 
void add(int u, int t) {
    e[++cnt] = (edge) {t,head[u]}; head[u] = cnt;
}
 
void dfs(int u, int F) {
    dep[u] = dep[F] + 1; 
    pos[u] = ++T; mn[T][0] = u;
    for (int i = head[u]; i; i = e[i].nxt) {
        int t = e[i].t;
        if (t == F) continue;
        dfs(t, u);
        mn[++T][0] = u;
    }
}
 
int lca(int x, int y) {
    x = pos[x]; y = pos[y];
    if (x > y) swap(x, y);
    int t = lg[y-x+1];
    return dep[mn[x][t]] < dep[mn[y-(1<<t)+1][t]] ? mn[x][t] : mn[y-(1<<t)+1][t];
}
 
int dist(int x, int y) {
    return dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
 
 
//==================================================
int Sz,rt,mx;
int siz[N],F[N],flg[N],in[N];
void getroot(int x, int f) {
    siz[x] = 1;
    int Bs=0;
    for (int i = head[x]; i; i = e[i].nxt) {
        int t = e[i].t;
        if (t == f || vis[t]) continue;
        getroot(t, x);
        siz[x] += siz[t];
        Bs = max(Bs, siz[t]);
    }
    Bs = max(Bs, Sz - siz[x]);
    if (Bs < mx) mx = Bs, rt = x;
}
 
#define pii pair
#define mk make_pair
#define fi first
#define se second
 
queue<pii> Q;
int h[N],nxt[N*30];
int Cnt,tp;
pii son[N*30],st[N];
void Add(int u, pii now) {
    son[++Cnt] = now; nxt[Cnt] = h[u]; h[u] = Cnt;
}
void bfs(int x) { 
    Q.push(mk(0, x));
    in[x] = x;
    while (!Q.empty()) {
        pii now = Q.front(); Q.pop();
        st[++tp] = now;
        int u = now.se, dep = now.fi;
        for (int i = head[u]; i; i = e[i].nxt) {
            int t = e[i].t;
            if (in[t] == x || vis[t]) continue;
            in[t] = x;
            Q.push(mk(dep+1, t));
        }
    }
    while (tp) Add(x, st[tp--]);
//  printf("%d\n",son[x].size());
}
 
void divide(int x) {
    vis[x] = 1;
//  for (int i = head[x]; i; i = e[i].nxt) if (!vis[e[i].t]) 
    bfs(x);
    for (int i = head[x]; i; i = e[i].nxt) {
        int t = e[i].t;
        if (vis[t]) continue;
        Sz = siz[t]; mx = 1e9;
        getroot(t, x);
        F[rt] = x;
        divide(rt);
    }
}
 
void delet(int x, int limit, ll v, int u) {
    if (lim < 0) return;
    while (h[x] && son[h[x]].fi <= limit) {
        int k = son[h[x]].se; h[x] = nxt[h[x]];
        if (flg[k]) continue;
        dis[k] = v, flg[k] = 1, q.push(k);
    }
    if (F[x]) delet(F[x], lim[u] - dist(u, F[x]), v, u);
}
int main() {
//  freopen("5129.in", "r", stdin);
//  freopen("5129.out", "w", stdout);
    n = read(); S = read();
    for (int i = 1, u, v; i < n; i++) {
        u = read(), v = read();
        add(u, v); add(v, u);
    }
    for (int i = 1; i <= n; i++) lim[i] = read(), cos[i] = read();
    dfs(1, 0); // get dis
    for (int i = 2; i <= T; i++) lg[i] = lg[i>>1] + 1;
    for (int i = 1; i <= 19; i++)
        for (int j = 1; j+(1<<i) <= T; j++)
            mn[j][i] = dep[mn[j][i-1]] < dep[mn[j+(1<<i-1)][i-1]] ? mn[j][i-1] : mn[j+(1<<i-1)][i-1];
     
    Sz = n; mx = 1e9;
    getroot(1, 0);
    divide(rt); // get divide tree
    dis[S] = 0; flg[S] = 1;
    q.push(S);
    while (!q.empty()) {
        int u = q.top(); q.pop();
        delet(u, lim[u], dis[u] + cos[u], u);
    }
    for (int i = 1; i <= n; i++) printf("%lld\n",dis[i]);
}

你可能感兴趣的:(点分树)