[LOJ#2553][CTSC2018]暴力写挂

[LOJ#2553][CTSC2018]暴力写挂

试题描述

在学习太极之后,Bob 要求 Alice 教他太极剑。Alice 告诉他首先需要通过一项基本剑术测试。测试要求 Bob 尽可能快地切断 \(n\) 根绳子。

所有绳子的端点两两不同,所以共有 \(2n\) 个端点。这些端点被捆在一个圆上,等距离分布。我们把这些端点按顺时针方向编号为 \(1\)\(2n\)

Bob 每次切割的轨迹是一条直线,可以将所有与这条直线相交的绳子切断,他想知道至少多少次可以切断所有的绳子。

输入

第一行一个整数 \(n(1 \leq n \leq 2 \times 10^5)\),表示绳子的个数。

接下来 \(n\) 行,每行两个整数 \(a_i, b_i(1 \leq a_i, b_i \leq 2n, a_i \not= b_i)\),表示第 \(i\) 根绳子的两个端点的编号。

输出

一行一个整数,表示答案。

输入示例

6
1 2 2
1 3 0
2 4 1
2 5 -7
3 6 0
1 2 -1
2 3 -1
2 5 3
2 6 -2
3 4 8

输出示例

5

数据规模及约定

对于所有数据, \(n \le 366666 , |v| \le 2017011328\)

题解

搁置已久的大锅终于搞掉了……这个题让自带大常数的我卡得生活不能自理……

将第一棵树转化成二叉树后边分治,那么考虑重心边的两侧一定有一侧是更“靠近”根的;假设集合 \(A\) 离根更近,集合 \(B\) 离根更远,那么 \(\forall x \in A\) 都有 \(\forall y \in B, y' \in B, lca(x, y) = lca(x, y')\),其中 \(lca(a, b)\) 表示在原树上 \(a\)\(b\) 的最近公共祖先。

那么这样我们可以枚举 \(x\),然后 \(depth(x) - depth(lca(x, y))\) 就固定了,我们就是要找到最小的 \(depth(y) - depth'(lca'(x, y))\),这个东西建一下虚树然后树形 dp 即可(正反两次 dp,一次从子树往上推,第二次从父节点更新到子节点)。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    return x * f;
}

#define maxn 800010
#define maxm 1600010
#define maxlog 21
#define pii pair <int, int>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)
#define LL long long

int Log[maxn<<1];
struct tree {
    int n, m, head[maxn], nxt[maxm], to[maxm], dist[maxm];
    
    tree(): m(0) { memset(head, 0, sizeof(head)); }
    
    void AddEdge(int a, int b, int c) {
        to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
        swap(a, b);
        to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
        return ;
    }
} Tmp;
struct Tree {
    int n, m, head[maxn], nxt[maxm], to[maxm], dist[maxm], id[maxm], dep[maxn], dfn[maxn], clo, mnp[maxlog][maxn<<1];
    LL Dep[maxn];
    
    Tree(): m(0) { memset(head, 0, sizeof(head)); }
    
    void AddEdge(int a, int b, int c, int Id = 0) {
        to[++m] = b; dist[m] = c; id[m] = Id; nxt[m] = head[a]; head[a] = m;
        swap(a, b);
        to[++m] = b; dist[m] = c; id[m] = Id; nxt[m] = head[a]; head[a] = m;
        return ;
    }
    
    void build(int u, int fa) {
        mnp[0][dfn[u] = ++clo] = u;
        for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
            dep[to[e]] = dep[u] + 1;
            Dep[to[e]] = Dep[u] + dist[e];
            build(to[e], u);
            mnp[0][++clo] = u;
        }
        return ;
    }
    void rmq_init() {
        Log[1] = 0;
        rep(i, 2, clo) Log[i] = Log[i>>1] + 1;
        rep(i, 1, Log[clo])
            rep(j, 1, clo - (1 << i) + 1) {
                int a = mnp[i-1][j], b = mnp[i-1][j+(1<<i>>1)];
                mnp[i][j] = dep[a] < dep[b] ? a : b;
            }
        return ;
    }
    int lca(int a, int b) {
        int l = dfn[a], r = dfn[b];
        if(l > r) swap(l, r);
        int t = Log[r-l+1], A = mnp[t][l], B = mnp[t][r-(1<<t)+1];
        return dep[A] < dep[B] ? A : B;
    }
} T, T1;

namespace rebuildTree {
    int sons[maxn], sonv[maxn], cs, M;
    void getT(int u, int fa) {
        for(int e = Tmp.head[u]; e; e = Tmp.nxt[e]) if(Tmp.to[e] != fa) getT(Tmp.to[e], u);
        cs = 0;
        for(int e = Tmp.head[u]; e; e = Tmp.nxt[e]) if(Tmp.to[e] != fa) sons[++cs] = Tmp.to[e], sonv[cs] = Tmp.dist[e];
        // printf("%d has %d sons\n", u, cs);
        if(cs <= 2) rep(i, 1, cs) T.AddEdge(u, sons[i], sonv[i], ++M);
        else {
            T.n++; T.AddEdge(T.n, sons[1], sonv[1], ++M); T.AddEdge(T.n, sons[2], sonv[2], ++M);
            rep(i, 3, cs - 1) T.n++, T.AddEdge(T.n, T.n - 1, 0, ++M), T.AddEdge(T.n, sons[i], sonv[i], ++M);
            T.AddEdge(u, T.n, 0, ++M); T.AddEdge(u, sons[cs], sonv[cs], ++M);
        }
        return ;
    }
    
    void build() {
        getT(1, 0);
        // printf("M: %d, %d\n", M, T.n);
        T.build(1, 0); T1.build(1, 0);
        T.rmq_init(); T1.rmq_init();
        return ;
    }
}

namespace Solve {
    const LL ool = 1ll << 60;
    
    LL ans = -ool;
    
    namespace Vtree {
        int KeyPoint[maxn], K;
        int m, head[maxn], nxt[maxm], to[maxm];
        LL val[maxn], extra[maxn];
        
        void clear() {
            rep(i, 1, K) val[KeyPoint[i]] = extra[KeyPoint[i]] = -ool, head[KeyPoint[i]] = 0;
            K = m = 0;
            return ;
        }
        
        void AddEdge(int a, int b) {
            to[++m] = b; nxt[m] = head[a]; head[a] = m;
            return ;
        }
        
        LL f[maxn], pre[maxn], suf[maxn], sonv[maxn];
        int son[maxn], cs;
        void dp(int u, int fa) {
            f[u] = val[u];
            for(int e = head[u]; e; e = nxt[e]) dp(to[e], u), f[u] = max(f[u], f[to[e]]);
            cs = 0;
            for(int e = head[u]; e; e = nxt[e]) son[++cs] = to[e], sonv[cs] = f[to[e]];
            LL nmx = -ool;
            rep(i, 1, cs) {
                pre[son[i]] = nmx;
                nmx = max(nmx, sonv[i]);
            }
            nmx = -ool;
            dwn(i, cs, 1) {
                suf[son[i]] = nmx;
                nmx = max(nmx, sonv[i]);
            }
            return ;
        }
        void dp2(int u, int fa, LL nmx) {
            if(extra[u] > -ool && (f[u] > -ool || nmx > -ool)) ans = max(ans, extra[u] + max(f[u] - T1.Dep[u], nmx));
            for(int e = head[u]; e; e = nxt[e]) {
                LL now = max(max(pre[to[e]], suf[to[e]]), val[u]);
                dp2(to[e], u, max(nmx, now > -ool ? now - T1.Dep[u] : -ool));
            }
            return ;
        }
        
        bool cmp(const int &a, const int &b) { return T1.dfn[a] < T1.dfn[b]; }
        void build() {
            sort(KeyPoint + 1, KeyPoint + K + 1, cmp);
            rep(i, 1, K - 1) KeyPoint[++K] = T1.lca(KeyPoint[i], KeyPoint[i+1]);
            KeyPoint[++K] = T1.lca(KeyPoint[K], KeyPoint[1]);
            sort(KeyPoint + 1, KeyPoint + K + 1, cmp);
            K = unique(KeyPoint + 1, KeyPoint + K + 1) - KeyPoint - 1;
            rep(i, 1, K - 1) {
                int a = KeyPoint[i], b = KeyPoint[i+1], c = T1.lca(a, b);
                AddEdge(c, b);
            }
            dp(KeyPoint[1], 0); dp2(KeyPoint[1], 0, -ool);
            return ;
        }
    }
    using Vtree::KeyPoint;
    using Vtree::K;
    using Vtree::val;
    using Vtree::extra;
    
    pii rt;
    int eid, size, best, siz[maxn];
    bool vis[maxn];
    
    void getrt(int u, int fa, int fae) {
        siz[u] = 1;
        for(int e = T.head[u]; e; e = T.nxt[e]) if(T.id[e] != fae && !vis[T.id[e]]) {
            int v = T.to[e];
            getrt(v, u, T.id[e]);
            siz[u] += siz[v];
        }
        if(fa && best > max(siz[u], size - siz[u])) best = max(siz[u], size - siz[u]), rt = mp(u, fa), eid = fae;
        return ;
    }
    
    void dfs(int u, int fae, bool tp) {
        if(u <= T1.n) {
            KeyPoint[++K] = u;
            if(tp) val[u] = T.Dep[u];
            else extra[u] = T.Dep[u] - T.Dep[T.lca(u,rt.x)];
        }
        for(int e = T.head[u]; e; e = T.nxt[e]) if(T.id[e] != fae && !vis[T.id[e]]) dfs(T.to[e], T.id[e], tp);
        return ;
    }
    void solve(pii u, int ed) {
        // printf("%d(%d) -- %d(%d)\n", u.x, siz[u.x], u.y, siz[u.y]);
        vis[ed] = 1;
        if(T.dep[u.x] > T.dep[u.y]) swap(u.x, u.y);
        dfs(u.x, 0, 0); dfs(u.y, 0, 1);
        Vtree::build();
        Vtree::clear();
        
        if(siz[u.x] > 1) {
            rt = mp(0, 0); eid = 0; best = (size = siz[u.x]) + 1; getrt(u.x, 0, 0);
            if(eid) solve(rt, eid);
        }
        if(siz[u.y] > 1) {
            rt = mp(0, 0); eid = 0; best = (size = siz[u.y]) + 1; getrt(u.y, 0, 0);
            if(eid) solve(rt, eid);
        }
        return ;
    }
    
    void main() {
        rep(i, 1, T1.n) val[i] = extra[i] = -ool;
        rt = mp(0, 0); eid = 0; best = (size = T.n) + 1; getrt(1, 0, 0);
        solve(rt, eid);
        rep(i, 1, T1.n) ans = max(ans, T.Dep[i] - T1.Dep[i]);
        return ;
    }
}

int main() {
    Tmp.n = T.n = T1.n = read();
    rep(i, 1, Tmp.n - 1) {
        int a = read(), b = read(), c = read();
        Tmp.AddEdge(a, b, c);
    }
    rep(i, 1, T1.n - 1) {
        int a = read(), b = read(), c = read();
        T1.AddEdge(a, b, c);
    } // */
    
    rebuildTree::build();
    Solve::main();
    
    printf("%lld\n", Solve::ans);
    
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xiao-ju-ruo-xjr/p/9085258.html