HDU 5361 In Touch(并查集实现区间删除)

87 篇文章 0 订阅
10 篇文章 0 订阅

In Touch

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 2302    Accepted Submission(s): 617


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  (1in)
 

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   (1n2×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 (0lirin,1ci109)
 

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
Hint
If you need a larger stack size, please use #pragma comment(linker, "/STACK:102400000,102400000") and submit your solution using C++.
 

Author
zimpha@zju
 

Source
 

Recommend
wange2014

题目大意:

    有N个点排成一排,相邻两个点之间距离为1,每个点可以花费ci到达距离不小于li,不大于ri的点。求第一个点到其他点的距离。


解题思路:

    从最朴素的dijkstra开始思考,为了求出单源最短路,我们需要不断找出未确定的点中到原点距离最小的点。这个图中每个点的出边权值都一样,所以它所指向的点v通过这个点u的距离一定是dist[u]+cost[u],所以我们就可以用一个优先队列维护已经确定的点,dist[u]+cost[u]小的优先出队。但是这样还是不行,如果每个点所指向的区间非常大的话,复杂度最坏就能达到O(N^2 * log N)了。于是我们需要继续优化。

    按照刚才的方式更新,每个点一定是在第一次更新的时候dist最小,所以就可以找一个方法让它在第一次更新后就不再考虑。很容易想到的就是set,确实也有很多人是用set写的,不过还有更快的方法,就是用并查集。对于已经更新过的区间我们可以用一个并查集合并,根节点设定为区间最右边的下一个点。这样我们从左往右扫更新dist的时候,如果遇到更新过的区间就可以通过根节点直接跳过区间。那么总复杂度就是O(N * log N)。


AC代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <vector>
#include <queue>
#include <stack>
#include <deque>
#include <string>
#include <map>
#include <set>
#include <list>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))
#pragma comment(linker, "/STACK:102400000,102400000") 

const int MAXV=200000+3;
int V, l[MAXV], r[MAXV];
LL cost[MAXV];
LL dis[MAXV];
int par[MAXV];

struct Node
{
    int u;
    Node(int u):u(u){}
    bool operator < (const Node &other)const
    {
        return dis[u]+cost[u]>dis[other.u]+cost[other.u];
    }
};

void init()//初始化
{
    for(int i=0;i<=V;++i)//一定要多初始化一个点,因为会合并区间右端点的下一个点
    {
        dis[i]=INF;
        par[i]=i;
    }
}

int findfather(int x)
{
    return par[x]=par[x]==x?x:findfather(par[x]);
}

void unite(int a,int b)//让最右边的点做根节点
{
    int fa=findfather(a), fb=findfather(b);
    if(fa!=fb)
        par[fa]=fb;
}

void dijkstra(int s)
{
    priority_queue<Node> que;
    que.push(Node(s));
    dis[s]=0;
    unite(0,1);
    while(!que.empty())
    {
        int u=que.top().u; que.pop();
        for(int i=-1;i<2;i+=2)//分别处理左右两个区间
        {
            int left=u+l[u]*i, right=u+r[u]*i;
            if(left>right)
                swap(left, right);
            left=max(left, 0);
            right=min(right, V-1);
            for(;left<=right;++left)
            {
                left=findfather(left);//移动到已经更新过的区间的右边
                if(left>right)
                    break;
                dis[left]=dis[u]+cost[u];//这里一定是未更新的点
                que.push(Node(left));
                unite(left, left+1);
            }
        }
    }
}

int main()
{
    int T_T;
    scanf("%d",&T_T);
    while(T_T--)
    {
        scanf("%d", &V);
        init();
        for(int i=0;i<V;++i)
            scanf("%d",&l[i]);
        for(int i=0;i<V;++i)
            scanf("%d",&r[i]);
        for(int i=0;i<V;++i)
            scanf("%lld",&cost[i]);
        dijkstra(0);
        for(int i=0;i<V;++i)
            printf("%lld%c",dis[i]==INF?-1:dis[i], i==V-1?'\n':' ');
    }
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值