题意:求1到其余的点的最短路。只不过这个边有些特殊,是可以到达一个区间的范围的点。
思路:可以想到每个点最多更新一次,利用dij做法的话。跟平常dij不同的是,因为1个点到区间范围内的点的花费相同,座椅每次更新出来的最小值应当是dis[u]+cost[u]。堆应当按照这个排序,才能确保更新的值最优。
然后区间覆盖的问题,我们可以利用并查集来维护。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <cmath>
#include <queue>
using namespace std;
typedef long long LL;
const int MAXN = 2e5+7;
const LL inf = 1e16;
int n,m;
int l[MAXN],r[MAXN];
int c[MAXN];
LL ans[MAXN];
struct node
{
int v;
LL w;
bool operator < (const node &a)const
{
return w+c[v] > a.w+c[a.v];
}
node(int v,LL w)
{
this->v = v;
this->w = w;
}
};
int pre[MAXN];
int findx(int x)
{
return x == pre[x]?x:pre[x] = findx(pre[x]);
}
void bfs()
{
for(int i = 1; i <= n+1; ++i)
{
pre[i] = i;
ans[i] = inf;
}
priority_queue<node>q;
q.push(node(1,0));
ans[1] = 0;
while(!q.empty())
{
node u = q.top();
q.pop();
if(ans[u.v] < u.w )continue;
for(int i = -1; i <= 1; i+=2)
{
int l1 = u.v+i*l[u.v];
int r1 = u.v+i*r[u.v];
if(l1 > r1)swap(l1,r1);
l1 = max(1,l1);
r1 = min(n,r1);
if(l1 > r1)continue;
for(int v = l1; ; ++v)
{
v = findx(v);
if(v > n || v > r1)break;
if(ans[v] > ans[u.v] + c[u.v])
{
ans[v] = ans[u.v] + c[u.v];
q.push(node(v,ans[v]));
}
pre[findx(v)] = findx(v+1);
}
}
}
for(int i = 1; i <= n; ++i)
{
printf(i == n?"%I64d\n":"%I64d ",ans[i]==inf?-1:ans[i]);
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i = 1; i <= n; ++i)scanf("%d",&l[i]);
for(int i = 1; i <= n; ++i)scanf("%d",&r[i]);
for(int i = 1; i <= n; ++i)scanf("%d",&c[i]);
bfs();
}
return 0;
}