HDU5361 In Touch(线段树 + 最短路)

传送门
恰逢才做过VFK的A+B Problem,发现这道题也可以那样搞。区间连边的时候,我们就可以给那个区间在线段树对应的标号上连边。
线段树也可以不建出来,直接当做一个标号的合集,不占用内存,只用模拟在线段树上找区间的过程就可以了。
如果不清楚的话,可以看下A+B Problem的题解里面配有插图。
连好边之后就可以直接跑 Dijkstra
分析一下时间复杂度,线段树节点数是 O(N) 的,边数 O(NlogN) ,所以算法复杂度为 O(NlogN) 但是因为

#include <cstdio>
#include <cstring>
#define MAXN 200005
#define MAXM 5000005
#pragma comment(linker, "/STACK:102400000,102400000")
#define INF 0x3f3f3f3f3f3f3f3fLL
#define I64 long long
struct { int v, nxt, w; } e[MAXM << 1];
int Adj[MAXN * 6], c, n, L[MAXN], R[MAXN], N;
I64 C[MAXN];
inline void Add(int u, int v, I64 w) { ++ c; e[c].v = v; e[c].nxt = Adj[u]; e[c].w = w; Adj[u] = c; }
inline void GET(int &n) {
    static char c; n = 0;
    do c = getchar(); while('0' > c || c > '9');
    do (n*=10)+=c-'0',c=getchar(); while('0' <= c && c <= '9');
}
inline void GET(I64 &n) {
    static char c; n = 0;
    do c = getchar(); while('0' > c || c > '9');
    do (n*=10)+=c-'0',c=getchar(); while('0' <= c && c <= '9');
}
int LL, RR, F;
void Link(int i, int l, int r) {
    if(LL > r || l > RR) return;
    if(LL <= l && r <= RR) { Add(F, n + i, C[F]); return; }
    int mid = (l + r) >> 1;
    Link(i << 1,  l,  mid);
    Link(i<<1|1, mid+1, r);
}
void Build(int i, int l, int r) {
    if(l == r) { Add(n + i, l, 0); if(i + n > N) N = i+n; return; }
    Add(i + n, n + (i << 1), 0); Add(n + i, n + (i << 1|1), 0);
    int mid = (l + r) >> 1;
    Build(i<<1, l, mid); Build(i<<1|1, mid+1, r);
}
/***************************************/
I64 dis[MAXN * 6];
int minp[MAXN * 12];
bool used[MAXN * 6];
inline void pushup(int p) {
    int &r = minp[p];
    r = p * !used[p];
    if (dis[minp[p<<1]] < dis[r]) r = minp[p<<1];
    if (dis[minp[p<<1|1]] < dis[r]) r = minp[p<<1|1];
}
void relax(int p, I64 d) {
    if(d >= dis[p]) return;
    dis[p] = d;
    for(int i = p; i; i>>=1) pushup(i);
}
void finish(int p) {
    used[p] = 1;
    for(int i = p; i; i>>=1) pushup(i);
}
/***************以上这一段可以就当做堆优化**************/
void Dijkstra() {
    for(int i = 0; i <= N; ++ i)
        dis[i] = INF, used[i] = (minp[i] = 0);
    relax(1, 0);
    while(minp[1]) {
        int u = minp[1];
        finish(u);
        for(int i = Adj[u]; i; i = e[i].nxt)
            relax(e[i].v, dis[u] + (I64)e[i].w);
    }
}
int main() {
    int T; scanf("%d", &T);
    while(T --) {
        GET(n); c = N = 0; memset(Adj, 0, sizeof Adj);
        for(int i = 1; i <= n; ++ i) GET(L[i]);
        for(int i = 1; i <= n; ++ i) GET(R[i]);
        for(int i = 1; i <= n; ++ i) GET(C[i]);
        Build(1, 1, n);
        for(int i = 1; i <= n; ++ i) {
            LL = L[i]+i; RR = R[i]+i; F = i;
            Link(1, 1, n);
            RR = i-L[i]; LL = i-R[i];
            Link(1, 1, n);
        }
        Dijkstra();
        putchar('0');
        for(int i = 2; i <= n; ++ i) printf(" %I64d", dis[i] == INF ? -1 : dis[i]);
        puts("");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值