HDU5361 In Touch


题目描述:

In Touch
Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1634 Accepted Submission(s): 440

Problem Description

There are n soda living in a straight line. soda are numbered by 1,2,…,n from left to right. The distance between two adjacent soda is 1 meter. Every soda has a teleporter. The teleporter of i-th soda can teleport to the soda whose distance between i-th soda is no less than li and no larger than ri. The cost to use i-th soda’s teleporter is ci.

The 1-st soda is their leader and he wants to know the minimum cost needed to reach i-th soda (1≤i≤n).

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains an integer n (1≤n≤2×105), the number of soda.
The second line contains n integers l1,l2,…,ln. The third line contains n integers r1,r2,…,rn. The fourth line contains n integers c1,c2,…,cn. (0≤li≤ri≤n,1≤ci≤109)

Output

For each case, output n integers where i-th integer denotes the minimum cost needed to reach i-th soda. If 1-st soda cannot reach i-the soda, you should just output -1.

Sample Input

1
5
2 0 0 0 1
3 1 1 0 5
1 1 1 1 1

Sample Output

0 2 1 1 -1

题解:

本来是很躶的一道最短路,但是因为边很多,不能够直接用迪杰斯特拉,因此考虑成段成段一下更新的迪杰斯特拉思路.就是常用的线段树+迪杰斯特拉.就是说:我们每次取出线段树中的最小值(类比于优先队列里面的那个值),然后拿这个值来更新它可以更新的每一段,注意,每一段有一个lazy和一个min的维护,很简单.特别需要考虑的是,当一个点被取出来的时候应该被放置为used,来避免找最小值的时候被误考虑了.
以上是经典的线段树迪杰斯特拉.
法二:这题有一个特别的地方,每一个点向外面发射的边的权值都是一样的,那么我们可以确认,当前dist+cost最小的点,拿它去更新的所有的点都是最小的,且只会被更新一次,这一点类似于bfs的特性,我们可以在更新的时候直接相当于已经找到这一点的最优值,直接合并这个点(并查集的路径压缩).
注意与直接暴力的迪杰斯特拉的区别,正常的话,我们更新一个节点的时候并不知道它能够merge,这就造成了并查集的路径压缩并不是那么高效.而我们这样就可以满足只要更新到就直接删掉的模式.

重点:

(1)经典的线段树+迪杰斯特拉(注意及时pushDown)
(2)法二中,用并查集去压缩路径,并且更新及能删的特性.

代码:
//这是线段树+迪杰斯特拉
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const int maxn = 2e5+100;
const int MAX_NODE = 4*maxn+100;
const ll INF = 1e18;
int n, lin[maxn], rin[maxn];
ll cost[maxn], ans[maxn];

ll mi[MAX_NODE], lazy[MAX_NODE], used[MAX_NODE];

void pushUp(int rt)//写的比较小心...其实used也可以不用
{
    int lrt = (rt<<1), rrt = ((rt<<1)|1);
    if(used[lrt]==0&&used[rrt]==0)
        mi[rt] = min(mi[lrt], mi[rrt]);
    else if(used[lrt]==0)
        mi[rt] = mi[lrt];
    else if(used[rrt]==0)
        mi[rt] = mi[rrt];
    else
        mi[rt] = INF;
    if(used[lrt]&&used[rrt])
        used[rt] = 1;
}
void pushDown(int rt)//记住要多pushDown...
{
    int lrt = (rt<<1), rrt = ((rt<<1)|1);
    if(lazy[rt]==INF)
        return;
    ll key = lazy[rt];
    if(used[lrt]==0)
    {
        lazy[lrt] = min(lazy[lrt], key);
        mi[lrt] = min(mi[lrt], key);
    }
    if(used[rrt]==0)
    {
        lazy[rrt] = min(lazy[rrt], key);
        mi[rrt] = min(mi[rrt], key);
    }
}
void build(int rt, int l, int r)
{
    //printf("rt is %d  %d  %d\n", rt, l, r);
    if(l==r)
    {
        if(l==1)
        {
            mi[rt] = 0;
            used[rt] = 0;
        }
        else
        {
            mi[rt] = INF;
            used[rt] = 0;
        }
        lazy[rt] = INF;
        return;
    }
    lazy[rt] = INF;
    used[rt] = 0;
    mi[rt] = INF;
    int lrt = (rt<<1), rrt = ((rt<<1)|1), mid = (l+r)/2;
    build(lrt, l, mid);
    build(rrt, mid+1, r);
    pushUp(rt);
}

void update(int L, int R, ll val, int rt, int l, int r)
{
    if(lazy[rt] <= val||used[rt])
            return;
    if(L <= l && R >= r)
    {
        lazy[rt] = min(lazy[rt], val);
        mi[rt] = min(val, mi[rt]);
        return;
    }
    pushDown(rt);//关键,只要往下走,就pushDown,不然pushUP没意义
    int lrt = (rt<<1), rrt = ((rt<<1)|1), mid = (l+r)/2;
    if(L <= mid)
        update(L, R, val, lrt, l, mid);
    if(R >= mid+1)
        update(L, R, val, rrt, mid+1, r);
    pushUp(rt);
}
void query(int rt, int l, int r, ll &val, int &pos)
{
    if(used[rt])
        val=INF;
    if(l==r)
    {
        val = mi[rt];
        pos = l;
        used[rt] = 1;
        mi[rt] = INF;
        return;
    }
    pushDown(rt);//加油
    int lrt = (rt<<1), rrt = ((rt<<1)|1), mid = (l+r)/2;
    if(used[lrt]==0&&used[rrt]==0)
    {
        if(mi[lrt] <= mi[rrt])
            query(lrt, l, mid, val, pos);
        else
            query(rrt, mid+1, r, val, pos);
    }
    else if(used[lrt]==0)
    {
        query(lrt, l, mid, val, pos);
    }
    else if(used[rrt] == 0)
    {
        query(rrt, mid+1, r, val, pos);
    }
    else
    {
        val = INF;
    }
    pushUp(rt);
}

void solve()
{
    int pos;
    ll val;
    memset(ans, -1, sizeof(ans));
    build(1, 1, n);
    while(1)
    {
        query(1, 1, n, val, pos);
        if(val==INF)
            break;
        ans[pos] = val;
        int tmpl = pos-rin[pos], tmpr = pos - lin[pos];
        if(tmpr >= 1)
            update(max(1, tmpl), tmpr, val+cost[pos], 1, 1, n);
        tmpl = lin[pos] + pos;
        tmpr = rin[pos] + pos;
        if(tmpl <= n)
            update(tmpl, min(tmpr, n), val+cost[pos], 1, 1, n);
    }
    for(int i = 1; i<=n; i++)
    {
        printf("%I64d%c", ans[i], (i==n?'\n':' '));
    }
}

int main()
{
    freopen("7Gin.txt", "r", stdin);
    //freopen("7Gout.txt", "w", stdout);
    int ncase;
    scanf("%d", &ncase);
    while(ncase--)
    {
        scanf("%d", &n);
        for(int i = 1; i<=n; i++)
            scanf("%d", &lin[i]);
        for(int i = 1; i<=n; i++)
            scanf("%d", &rin[i]);
        for(int i = 1; i<=n; i++)
            scanf("%I64d", &cost[i]);
        solve();
    }
    return 0;
}
//法二,用更新的性质压缩路径
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(ll i = a;i < b;i++)
#define REP_D(i, a, b) for(ll i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const ll maxn = 2e5 + 100;
const ll INF = 1e18;

struct node
{
    ll dist, cost, u;
    node(ll _dist = 0, ll _cost = 0, ll _u = 0)
    {
        dist = _dist;
        cost = _cost;
        u = _u;
    }
    bool operator < (const node& b) const
    {
        return dist + cost > b.dist + b.cost;//这个排序准则并不满足迪杰斯特拉的性质,但是我们根本没有用迪杰斯特拉算法,而是像bfs算答案那样的贪心.
    }
};
ll dist[maxn], lin[maxn], rin[maxn], cost[maxn];
ll fa[maxn], n;
ll getFa(ll u)
{
    if(u==fa[u])
        return u;
    return fa[u] = getFa(fa[u]);
}
void getUnion(ll a, ll b)
{
    a = getFa(a);
    b = getFa(b);
    if(a==b)
        return;
    fa[a] = b;
}

void solve()
{
    priority_queue<node> que;
    for(ll i = 1; i<=n; i++)
        dist[i] = INF;
    for(ll i = 1; i<=n+1; i++)
        fa[i] = i;
    que.push(node(0, cost[1], 1));
    dist[1] = 0;
    getUnion(1, min(2ll,n));
    while(!que.empty())
    {
        ll u = que.top().u;
        ll len = que.top().dist;
        que.pop();
        ll tmpl, tmpr;
        tmpl = u-rin[u];
        tmpr = u-lin[u];
        if(tmpr >= 1)
        {
            ll now = max(1ll, tmpl);
            while(1)
            {
                now = getFa(now);
                if(now > tmpr)
                    break;
                if(dist[now] > len+cost[u])
                {
                    dist[now] = len+cost[u];
                    que.push(node(dist[now], cost[now], now));
                }
                getUnion(now, now+1);
                now = now+1;
            }
        }
        tmpl = u+lin[u];
        tmpr = u+rin[u];
        if(tmpl <= n)
        {
            ll now = tmpl;
            tmpr = min(n, tmpr);
            while(1)
            {
                now = getFa(now);
                if(now > tmpr)
                    break;
                if(dist[now] > len+cost[u])
                {
                    dist[now] = len+cost[u];
                    que.push(node(dist[now], cost[now], now));
                }
                getUnion(now, now+1);
                now = now+1;
            }
        }
    }
    for(ll i = 1; i<=n; i++)
    {
        if(dist[i]==INF)
            printf("-1");
        else
            printf("%I64d", dist[i]);
        printf("%c", (i==n?'\n':' '));
    }
}

int main()
{
    freopen("13Min.txt", "r", stdin);
    //freopen("1out.txt", "w", stdout);
    ll ncase;
    scanf("%I64d", &ncase);
    while(ncase--)
    {
        scanf("%I64d", &n);
        for(ll i = 1; i<=n; i++)
            scanf("%I64d", &lin[i]);
        for(ll i = 1; i<=n; i++)
            scanf("%I64d", &rin[i]);
        for(ll i = 1; i<=n; i++)
            scanf("%I64d", &cost[i]);
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值