HDU 5361 In Touch (2015 多校6 1009)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5361

题意:最短路,求源点到所有点的最短距离。但与普通最短路不同的是,给出的边是某点到区间[l,r]内任意点的距离。

输入一个n,代表n个点,输入n个l[i],输入n个r[i],输入n个c[i]。

对于i,表示i到区间[i - r[i]],i - l[i]]和区间[i + l[i],i + r[i]]内的任意点的距离为c[i]。

求1到各个点的最短距离。

思路:若建边跑最短路的话,因为边过多,所以不可行。因为在最后的结果中,可能源点1到某段区间的最短路径都是一样的,所以可以在最短路的算法基础上利用线段树区间更新来维护源点1到区间的最短路径,最后只需要查询区间即可。线段树的每个结点存该区间源点到该区间点的最短路的最大值和最小值。所以叶子结点即是源点1到该点的最短路径。

代码:

#include <iostream>
#include <iomanip>
#include <stdio.h>
#include <string>
#include <string.h>
#include <math.h>
#include <queue>
#include <set>
#include <vector>
#include <algorithm>

using namespace std;

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

const long long INF = 1e18;
const int N = 2e5 + 10;

struct Segment {
    int l, r;
    long long d;

    Segment(int l = 0, int r = 0, long long d = 0) {
        this -> l = l;
        this -> r = r;
        this -> d = d;
    }

    friend bool operator < (Segment a, Segment b) {
        return a.d > b.d;
    }
};

struct Node {
    int used;
    long long _max;
    long long _min;
};

Node node[N << 2];
long long lazy[N << 2];
int n;
int lef[N];
int rig[N];
long long c[N];
priority_queue<Segment> q;

void pushup(int rt) {
    node[rt]._max = max(node[rt << 1]._max, node[rt << 1 | 1]._max);
    node[rt]._min = min(node[rt << 1]._min, node[rt << 1 | 1]._min);
    if (node[rt << 1].used == node[rt << 1 | 1].used)
        node[rt].used = node[rt << 1].used;
    else
        node[rt].used = -1;
}

void pushdown(int rt) {
    if (lazy[rt] != -1) {
        lazy[rt << 1] = lazy[rt << 1 | 1] = lazy[rt];
        node[rt << 1]._min = node[rt << 1]._max = lazy[rt];
        node[rt << 1 | 1]._min = node[rt << 1 | 1]._max = lazy[rt];
        lazy[rt] = -1;
    }
}

void build(int l, int r, int rt) {
    node[rt]._max = INF;
    node[rt]._min = INF;
    node[rt].used = 0;
    lazy[rt] = -1;
    if (l == r) {
        if (l == 1) {
            node[rt]._max = 0;
            node[rt]._min = 0;
        }
        return ;
    }
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    pushup(rt);
}

void update(long long cr, int L, int R, int l, int r, int rt) {
    if (node[rt]._max <= cr)
        return ;
    if (L <= l && r <= R) {
        node[rt]._max = cr;
        if (node[rt]._min > cr) {
            node[rt]._min = cr;
            q.push(Segment(l, r, cr));
            lazy[rt] = cr;
            return ;
        }
    }
    if (l == r)
        return ;
    pushdown(rt);
    int m = (l + r) >> 1;
    if (L <= m)
        update(cr, L, R, lson);
    if (R > m)
        update(cr, L, R, rson);
    pushup(rt);
}

void querysegment(Segment ff, int l, int r, int rt) {
    if (node[rt].used == 1)
        return ;
    if (ff.l <= l && r <= ff.r) {
        if (node[rt].used == 0) {
            for (int i = l; i <= r; i++) {
                int le = i + lef[i];
                int ri = min(n, i + rig[i]);
                if (le <= n) {
                    update(ff.d + c[i], le, ri, 1, n, 1);
                }
                le = max(1, i - rig[i]);
                ri = i - lef[i];
                if (ri >= 1) {
                    update(ff.d + c[i], le, ri, 1, n, 1);
                }
            }
            node[rt].used = 1;
            return ;
        }
    }
    if (l == r)
        return ;
    int m = (l + r) >> 1;
    if (ff.l <= m)
        querysegment(ff, lson);
    if (ff.r > m)
        querysegment(ff, rson);
    pushup(rt);
}

bool fir;
void query(int l, int r, int rt) {
    if (node[rt]._max == node[rt]._min) {
        for (int i = l; i <= r; i++) {
            if (node[rt]._min == INF)
                node[rt]._min = -1;
            if (!fir) {
                printf("%lld", node[rt]._min);
                fir = true;
            }
            else
                printf(" %lld", node[rt]._min);
        }
        return ;
    }
    if (l == r) {
        return ;
    }
    pushdown(rt);
    int m = (l + r) >> 1;
    query(lson);
    query(rson);
}

int main() {

    int t_case;
    scanf("%d", &t_case);
    for (int i_case = 1; i_case <= t_case; i_case++) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &lef[i]);
        for (int i = 1; i <= n; i++)
            scanf("%d", &rig[i]);
        for (int i = 1; i <= n; i++)
            scanf("%lld", &c[i]);
        build(1, n, 1);
        while(!q.empty())
            q.pop();

        q.push(Segment(1, 1, 0));
        while (!q.empty()) {
            Segment ff = q.top();
            q.pop();
            querysegment(ff, 1, n, 1);
        }

        fir = false;
        query(1, n, 1);
        printf("\n");
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-24 03:34:30

HDU 5361 In Touch (2015 多校6 1009)的相关文章

Hdu 5361 In Touch (dijkatrs+优先队列)

题目链接: Hdu 5361  In Touch 题目描述: 有n个传送机排成一排,编号从1到n,每个传送机都可以把自己位置的东西传送到距离自己[l, r]距离的位置,并且花费c,问从1号传送机到其他传送机的最小花费是多少?? 解题思路: 看了题解发现是dijkstra(求最短单源路径)+并查集(优化),刚开始的时候并不知道怎么用并查集优化,然后就提交了一个没有并查集的代码,果断TLE.然后开始搜代码,突然惊叹真是优化的太美妙. 因为每次从队列中取出的都是最优的,所以更新dis的时候可以直接把这

HDU 5301 Buildings(2015多校第二场)

Buildings Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 759    Accepted Submission(s): 210 Problem Description Your current task is to make a ground plan for a residential building located

HDU 5361 In Touch (使用SET集合)

In Touch Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 1184    Accepted Submission(s): 313 Problem Description There are n soda living in a straight line. soda are numbered by1,2,-,n from l

hdu 5289 Assignment(2015多校第一场第2题)RMQ+二分(或者multiset模拟过程)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5289 题意:给你n个数和k,求有多少的区间使得区间内部任意两个数的差值小于k,输出符合要求的区间个数 思路:求出区间的最大最小值,只要他们的差值小于k,那么这个区间就符合要求,但是由于n较大,用暴力一定超时,所以就要用别的方法了:而RMQ是可以求区间的最值的,而且预处理的复杂度只有O(nlogn),而查询只是O(1)处理,这样相对来说节约了时间,再根据右端点来二分枚举左端点(其实不用二分好像更快,估

hdu 5305 Friends(2015多校第二场第6题)记忆化搜索

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5305 题意:给你n个人,m条关系,关系可以是online也可以是offline,让你求在保证所有人online关系的朋友和offline关系的朋友相等的情况下,这样的情况有多少种. 思路:因为online关系和offline关系的人数相等,而且m最多才28,所以只要枚举每个人的一半的关系是否符合要求即可,而且根据题意m是奇数或者有一个人的总关系为奇数那么就没有符合要求的情况,这样可以排除很多情况.

hdu 5317 RGCDQ (2015多校第三场第2题)素数打表+前缀和相减求后缀(DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5317 题意:F(x) 表示x的不同质因子的个数结果是求L,R区间中最大的gcd( F(i) , F(j) ),i.j在L,R区间内. 思路:因为2<=L < R<=1000000,所以他们的质因子最多的个数也就7个,也就是说1<=F(x)<=7,因为要求最大的GCD,所以只要知道在L,R区间内每个F(x)的情况就可以知道结果. 代码: 1 #include <stdio.h

hdu 5358 First One 2015多校联合训练赛#6 枚举

First One Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 142    Accepted Submission(s): 37 Problem Description soda has an integer array a1,a2,-,an. Let S(i,j) be the sum of ai,ai+1,-,aj. No

hdu 5316 Magician(2015多校第三场第1题)线段树单点更新+区间合并

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5316 题意:给你n个点,m个操作,每次操作有3个整数t,a,b,t表示操作类型,当t=1时讲a点的值改成b:当t=0时,查询区间a,b之间最大的子序列和,这个子序列中的相邻的元素的原来的下标奇偶性都不同. 思路:这道题难点就在查询,其余都是模板,而根据查询,你只要分别把下一个区间的奇偶最大的情况分别比较,合并到上一个区间这样可以构建一个每个节点存有区间中奇开头偶开头,奇结尾,偶结尾这些区间情况的树.

HDU 5316 Magician(2015多校第三场 线段树)

Magician Time Limit: 18000/9000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 1700    Accepted Submission(s): 503 Problem Description Fantasy magicians usually gain their ability through one of three usual methods