HDU - 5770 Treasure 思维 + 扫描线 + 线段树 (看题解)

HDU - 5770

没想出来, 感觉不应该啊, 没有想到转换成二维上的点的问题。

对于对钥匙和宝藏(u, v), 如果lca != u && lca != v 那么起点从u子树出发, 终点在v子树就能得到贡献。

子树在dfs序下是连续一段, 所以就对应到二维平面一个矩形加上一个数值, 求值最大的点。

对于lca == u || lca == v同样可以讨论出来。

还有一种情况就是u == v, 我们先把贡献都加上, 然后对于不经过u 的所有路径进去这个贡献。

然后扫描线扫一遍就好了。

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define LL long long
#define LD long double
#define ull unsigned long long
#define fi first
#define se second
#define mk make_pair
#define PLL pair<LL, LL>
#define PLI pair<LL, int>
#define PII pair<int, int>
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define fio ios::sync_with_stdio(false); cin.tie(0);

using namespace std;

const int N = 1e5 + 7;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = (int)1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1);

template<class T, class S> inline void add(T& a, S b) {a += b; if(a >= mod) a -= mod;}
template<class T, class S> inline void sub(T& a, S b) {a -= b; if(a < 0) a += mod;}
template<class T, class S> inline bool chkmax(T& a, S b) {return a < b ? a = b, true : false;}
template<class T, class S> inline bool chkmin(T& a, S b) {return a > b ? a = b, true : false;}

const int LOG = 17;

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1

struct SegmentTree {
    int mx[N << 2], lazy[N << 2];
    void build(int l, int r, int rt) {
        mx[rt] = lazy[rt] = 0;
        if(l == r) return;
        int mid = l + r >> 1;
        build(lson); build(rson);
    }
    inline void push(int rt) {
        if(lazy[rt]) {
            mx[rt << 1] += lazy[rt];
            mx[rt << 1 | 1] += lazy[rt];
            lazy[rt << 1] += lazy[rt];
            lazy[rt << 1 | 1] += lazy[rt];
            lazy[rt] = 0;
        }
    }
    void update(int L, int R, int val, int l, int r, int rt) {
        if(R < l || r < L || R < L) return;
        if(L <= l && r <= R) {
            mx[rt] += val;
            lazy[rt] += val;
            return;
        }
        push(rt);
        int mid = l + r >> 1;
        update(L, R, val, lson);
        update(L, R, val, rson);
        mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]);
    }
    inline int getMax() {
        return mx[1];
    }
} Tree;

int n, m;
int sig_val;
int add_val[N];
int in[N], ot[N], idx;
int depth[N], pa[N][LOG];
vector<int> G[N];

void dfs(int u, int fa) {
    depth[u] = depth[fa] + 1;
    pa[u][0] = fa;
    in[u] = ++idx;
    for(int i = 1; i < LOG; i++) {
        pa[u][i] = pa[pa[u][i - 1]][i - 1];
    }
    for(auto &v : G[u]) {
        if(v == fa) continue;
        dfs(v, u);
    }
    ot[u] = idx;
}

int getLca(int u, int v) {
    if(depth[u] < depth[v]) swap(u, v);
    int d = depth[u] - depth[v];
    for(int i = LOG - 1; i >= 0; i--) {
        if(d >> i & 1) {
            u = pa[u][i];
        }
    }
    if(u == v) return u;
    for(int i = LOG - 1; i >= 0; i--) {
        if(pa[u][i] != pa[v][i]) {
            u = pa[u][i];
            v = pa[v][i];
        }
    }
    return pa[u][0];
}

inline int go(int u, int step) {
    for(int i = LOG - 1; i >= 0; i--) {
        if(step >> i & 1) {
            u = pa[u][i];
        }
    }
    return u;
}

int L_cnt;
struct Line {
    int x, y1, y2, val;
    bool operator < (const Line &rhs) const {
        return x < rhs.x;
    }
} L[N * 10];

void init() {
    idx = L_cnt = sig_val = 0;
    for(int i = 1; i <= n; i++) {
        add_val[i] = 0;
        G[i].clear();
    }
    Tree.build(1, n, 1);
}

int main() {
    int cas = 0;
    int T; scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        init();
        for(int i = 1; i < n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1, 0);
        for(int i = 1; i <= m; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            int lca = getLca(u, v);
            if(u == v) {
                sig_val += w;
                add_val[u] -= w;
            }
            else {
                if(u == lca) {
                    u = go(v, depth[v] - depth[u] - 1);
                    if(in[u] > 1) {
                        L[++L_cnt] = Line{1, in[v], ot[v], w};
                        L[++L_cnt] = Line{in[u], in[v], ot[v], -w};
                    }
                    if(ot[u] < n) {
                        L[++L_cnt] = Line{ot[u] + 1, in[v], ot[v], w};
                        L[++L_cnt] = Line{n + 1, in[v], ot[v], -w};
                    }
                }
                else if(v == lca) {
                    v = go(u, depth[u] - depth[v] - 1);
                    if(in[v] > 1) {
                        L[++L_cnt] = Line{in[u], 1, in[v] - 1, w};
                        L[++L_cnt] = Line{ot[u] + 1, 1, in[v] - 1, -w};
                    }
                    if(ot[v] < n) {
                        L[++L_cnt] = Line{in[u], ot[v] + 1, n, w};
                        L[++L_cnt] = Line{ot[u] + 1, ot[v] + 1, n, -w};
                    }
                }
                else {
                    L[++L_cnt] = Line{in[u], in[v], ot[v], w};
                    L[++L_cnt] = Line{ot[u] + 1, in[v], ot[v], -w};
                }
            }
        }
        for(int u = 1; u <= n; u++) {
            if(add_val[u]) {
                for(auto &v : G[u]) {
                    if(v == pa[u][0]) continue;
                    L[++L_cnt] = Line{in[v], in[v], ot[v], add_val[u]};
                    L[++L_cnt] = Line{ot[v] + 1, in[v], ot[v], -add_val[u]};
                }
                if(pa[u][0]) {
                    int v = u;
                    L[++L_cnt] = Line{1, 1, in[v] - 1, add_val[u]};
                    L[++L_cnt] = Line{1, ot[v] + 1, n, add_val[u]};
                    L[++L_cnt] = Line{in[v], 1, in[v] - 1, -add_val[u]};
                    L[++L_cnt] = Line{in[v], ot[v] + 1, n, -add_val[u]};

                    L[++L_cnt] = Line{ot[v] + 1, 1, in[v] - 1, add_val[u]};
                    L[++L_cnt] = Line{ot[v] + 1, ot[v] + 1, n, add_val[u]};
                }
            }
        }
        int ans = -inf;
        sort(L + 1, L + 1 + L_cnt);
        for(int i = 1, j = 1; i <= n; i++) {
            while(j <= L_cnt && L[j].x <= i) {
                if(L[j].y1 <= L[j].y2) {
                    Tree.update(L[j].y1, L[j].y2, L[j].val, 1, n, 1);
                }
                j++;
            }
            chkmax(ans, sig_val + Tree.getMax());
        }
        printf("Case #%d: ", ++cas);
        printf("%d\n", ans);
    }
    return 0;
}

/*
*/

原文地址:https://www.cnblogs.com/CJLHY/p/11512419.html

时间: 2024-10-07 06:11:49

HDU - 5770 Treasure 思维 + 扫描线 + 线段树 (看题解)的相关文章

HDU - 1542 Atlantis (扫描线+线段树)

There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to

HDU 3642 - Get The Treasury - [加强版扫描线+线段树]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description Jack knows that there is a great underground treasury in a secret region. And he has a special d

HDU 1255 覆盖的面积 (扫描线 线段树 离散化)

题目链接 题意:中文题意. 分析:纯手敲,与上一道题目很相似,但是刚开始我以为只是把cnt>=0改成cnt>=2就行了,. 但是后来发现当当前加入的线段的范围之前 还有线段的时候就不行了,因为虽然现在都不等于 2,但是之前的那个线段加上现在的已经覆盖2次了. 1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <cstring> 5 #include &

HDU 3642 Get The Treasury 线段树+扫描线

反向标记是错的,要对矩形进行拆分 #include <cstdio> #include <algorithm> #include <cstring> #include <vector> typedef long long LL; using namespace std; #define lson rt << 1,l,mid #define rson rt << 1 | 1,mid + 1,r const int maxn = 5e4

HDU 1264 Counting Squares (线段树-扫描线-矩形面积并)

Problem A : Counting Squares From:HDU, 1264 Problem Description Your input is a series of rectangles, one per line. Each rectangle is specified as two points(X,Y) that specify the opposite corners of a rectangle. All coordinates will be integers in t

hdu 1255 覆盖的面积(线段树&amp;扫描线&amp;重复面积)

覆盖的面积 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3375    Accepted Submission(s): 1645 Problem Description 给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. Input 输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数

hdu 1255 覆盖的面积 线段树扫描线求重叠面积

稀里糊涂打完了没想到1A了,心情还是很舒畅的,c和c++的四舍五入还是四舍六入遇积进位遇偶舍,我感觉很混乱啊,这道题我输出的答案是7.62,也就是遇偶舍了,可是我就手动处理一下进位问题,发现0.005 系统自动进位0.01了,尼玛啊,我一下子就混乱了,不是遇偶舍么,0也是偶数啊,怎么就进位了呢.最后我就不手动处理进位问题了,直接0.2lf%,虽然我输出的结果是7.62,但是提交也过了 这道题和poj1151,hdu1542差不多,扫描线详细讲解http://blog.csdn.net/young

HDU 1394 Minimum Inversion Number.(线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 ~~~~ 早起一发线段树,开心又快乐.这题暴力也能水过,同时线段树的效率也就体现的尤为明显了,看了大牛的博客,说是还可以用树状数组,点树和合并序列写,现在还不懂,留着以后在写吧. ~~~~ 大致题意:给定一个数字序列,同时由此可以得到n个序列, 要求从n个序列中找到逆序数最小的序列,输出最小逆序数. 首先介绍下逆序数的概念: 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面

HDU1255_覆盖的面积(扫描线/线段树+离散)

解题报告 题目传送门 题意: 求面积交. 思路: 不会呀. 只知道线段树应该维护覆盖数大于2的线段长度. 不会更新,看了别人写的理解的,太菜了. 用sum1和sum2分别来表示覆盖数为1的区间长度和覆盖数为2的区间长度. 更新时即要更新sum1也要更新sum2: 区间如果被覆盖 sum1为实际区间长度,如果覆盖一次,sum2为左右子树的sum1和,覆盖两次就为实际区间长度. 没有被覆盖就直接等于左右子树的和. #include <algorithm> #include <iostream