Luogu 4069 [SDOI2016]游戏

BZOJ 4515

树链剖分 + 李超线段树

要求支持区间插入一条线段,然后查询一个区间内的最小值。可以使用李超线段树解决,因为要维护一个区间内的最小值,所以每一个结点再维护一个$res$表示这个区间内的最小值。因为本题把问题搬到了树上,剖一下就可以了。

我们可以把一个点$x$到根的距离$dis_x$记为一个点在二维平面上的横坐标,这样子可以保证一条重链上的点的距离递增,并且不改变点之间的距离。

考虑到一条树链$(x, y, z)$($z$是$lca(x, y)$)上的数字的覆盖情况是先从$x$向上走到$z$,然后从$z$向下走走到$y$,向上走的时候直线的斜率是$-k$,而向下走的时候直线的斜率是$k$,所以我们把一条树链拆成两条线段插入。

现在记录剩下的我写错的地方:

1、链剖的$dfs2$写错了,一定要注意先向下剖分重儿子。

2、在写线段树的时候要注意要求出来的线段交点的坐标其实是没有离散化过的值,需要拿它和中间的坐标的原来的值进行比较。

3、在求最小的时候注意上层的结点的区间可能包含了我们要求的区间,而这时候上层结点的$res$并不能拿来直接使用(可能会使答案变小),需要根据保存的直线的解析式重新计算。

时间复杂度$O(nlog^3n)$,然后只要相信它常数很小就好了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef double db;

const int N = 1e5 + 5;
const ll Val0 = 123456789123456789LL;

int n, qn, tot = 0, head[N];
int dfsc = 0, id[N], pos[N], fa[N], top[N], dep[N], siz[N], son[N];
ll dis[N], kk[N << 1], bb[N << 1];

struct Edge {
    int to, nxt;
    ll val;
} e[N << 1];

inline void add(int from, int to, ll val) {
    e[++tot].to = to;
    e[tot].val = val;
    e[tot].nxt = head[from];
    head[from] = tot;
}

template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for(; ch > '9'|| ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

template <typename T>
inline void chkMin(T &x, T y) {
    if(y < x) x = y;
}

void dfs1(int x, int fat, int depth, ll nowDis) {
    fa[x] = fat, dep[x] = depth, siz[x] = 1, dis[x] = nowDis;
    int maxson = -1;
    for(int i = head[x]; i; i = e[i].nxt) {
        int y = e[i].to;
        if(y == fat) continue;
        dfs1(y, x, depth + 1, nowDis + e[i].val);
        
        siz[x] += siz[y];
        if(siz[y] > maxson)
            maxson = siz[y], son[x] = y;
    }
}

void dfs2(int x, int topf) {
    top[x] = topf, pos[id[x] = ++dfsc] = x;
    if(!son[x]) return;
    dfs2(son[x], topf);
    for(int i = head[x]; i; i = e[i].nxt) {
        int y = e[i].to;
        if(y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
    }
}

inline int getLca(int x, int y) {
    for(; top[x] != top[y]; ) {
        if(dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
    }
    return dep[x] > dep[y] ? y : x;
}

/* inline ll getDis(int x, int y) {
    ll z = getLca(x, y);
    return dis[x] + dis[y] - 2 * dis[z];
}   */

namespace SegT {
    struct Node {
        int id;
        ll res;
    } s[N << 2];
    
    #define lc p << 1
    #define rc p << 1 | 1
    #define mid ((l + r) >> 1)
    #define res(p) s[p].res
    
    inline void up(int p) {
//        res(p) = Val0;
        chkMin(res(p), res(lc)), chkMin(res(p), res(rc));
    }
    
    void build(int p, int l, int r) {
        res(p) = Val0, s[p].id = 0;
        if(l == r) return;
        
        build(lc, l, mid);
        build(rc, mid + 1, r);
    }
        
    void ins(int p, int l, int r, int x, int y, int nid) {
        if(x <= l && y >= r) {
/*            if(!s[p].cov) {
                s[p].cov = 1;
                s[p].id = nid;
                chkMin(res(p), min(l1, r1));
                return;
            }    */
            
            ll l1 = kk[nid] * dis[pos[l]] + bb[nid], r1 = kk[nid] * dis[pos[r]] + bb[nid];
            ll l2 = kk[s[p].id] * dis[pos[l]] + bb[s[p].id], r2 = kk[s[p].id] * dis[pos[r]] + bb[s[p].id];
            if(l1 >= l2 && r1 >= r2) return;
            if(l1 <= l2 && r1 <= r2) {
                s[p].id = nid;
                chkMin(res(p), min(l1, r1));
                return;
            }
            
            db loc = 1.0 * (bb[nid] - bb[s[p].id]) / (kk[s[p].id] - kk[nid]);
            db md = (db) dis[pos[mid]];
            if(l1 > l2) {
                if(loc > md) ins(rc, mid + 1, r, x, y, nid);
                else ins(lc, l, mid, x, y, s[p].id), s[p].id = nid;
            } else {
                if(loc > md) ins(rc, mid + 1, r, x, y, s[p].id), s[p].id = nid;
                else ins(lc, l, mid, x, y, nid);
            }
            
            chkMin(res(p), min(l1, r1));
            up(p);        
            return;
        }
        
        if(x <= mid) ins(lc, l, mid, x, y, nid);
        if(y > mid) ins(rc, mid + 1, r, x, y, nid);
        up(p);
    }
    
    ll query(int p, int l, int r, int x, int y) {
        if(x <= l && y >= r) return res(p);
        ll res = Val0;
        if(s[p].id) {
            ll ld = dis[pos[max(l, x)]], rd = dis[pos[min(y, r)]];
            chkMin(res, min(ld * kk[s[p].id], rd * kk[s[p].id]) + bb[s[p].id]);
        }
        if(x <= mid) chkMin(res, query(lc, l, mid, x, y));
        if(y > mid) chkMin(res, query(rc, mid + 1, r, x, y));
        return res;
    }
    
} using namespace SegT;

inline void solve(int x, int y) {
    ll res = Val0;
    for(; top[x] != top[y]; ) {
        if(dep[top[x]] < dep[top[y]]) swap(x, y);
        chkMin(res, query(1, 1, n, id[top[x]], id[x]));
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x, y);
    chkMin(res, query(1, 1, n, id[x], id[y]));
    printf("%lld\n", res);
}

inline void addSeg(int x, int y, int nid) {
    for(; top[x] != top[y]; ) {
//        if(dep[top[x]] < dep[top[y]]) swap(x, y);
        ins(1, 1, n, id[top[x]], id[x], nid);
        x = fa[top[x]];
    }
//    if(dep[x] > dep[y]) swap(x, y);
    ins(1, 1, n, id[y], id[x], nid);
}

int main() {
    read(n), read(qn);
    for(int i = 1; i < n; i++) {
        int x, y; ll v;
        read(x), read(y), read(v);
        add(x, y, v), add(y, x, v);
    }
    
    dfs1(1, 0, 1, 0LL), dfs2(1, 1);
    
/*    for(int i = 1; i <= n; i++)
        printf("%lld ", dis[pos[i]]);
    printf("\n");   */
    
    build(1, 1, n);
    bb[0] = Val0, kk[0] = 0LL;
    for(int op, x, y, cnt = 0; qn--; ) {
        read(op), read(x), read(y);
        if(op == 1) {
            ll k, b;
            read(k), read(b);
            
            int z = getLca(x, y);
            ++cnt;
            kk[cnt] = -k, bb[cnt] = b + dis[x] * k;
            addSeg(x, z, cnt);
            
            ++cnt;
            kk[cnt] = k, bb[cnt] = b + (dis[x] - 2LL * dis[z]) * k;
            addSeg(y, z, cnt);
        } else solve(x, y);
    }
    
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/CzxingcHen/p/10097431.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值