题目描述:
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;
}