the Red Sun

题面

Description

给定一张 N 个点的图, 点的标号为 1 到 n . 我们进行 M 次连边, 每次连边可以描述为 a b c d w :

for i = a to b do
    for j = c to d do
        Add_Edge(i,j,w)        

Add_Edge(i,j,w) 表示从点 i 向点 j 连一条费用为 w 的双向边.
求点 1 到点 n 的最小花费.
为了降低难度, 我们有 K 次机会可以消除某条边的花费.

Input

第一行一个数 T , 表示测试数据组数. 出于某种原因, T=1 .
第二行三个数 N,M,K .
接下来 M 行, 每行 5 个数 a,b,c,d,w , 意义如 题目描述 所示.

Output

一行一个数, 为最小的花费.
如果点 1 与点 n 不连通, 输出 "Yww is our red sun!" .

Sample Input

1
5 3 0
1 2 4 5 42
5 5 4 4 468
1 1 3 3 335

Sample Output

42

HINT

\(T=1\), \(1≤N≤5×10^4\), \(1≤M≤10^4\), \(0≤K≤10\), \(1≤w≤10^3\)

题解

考虑到题目要求的是一个区间的点和另一个区间的点连边, 我们可以建立两棵线段树来维护这些区间.
我们建立的两棵线段树, 一棵叫作源线段树, 一棵叫作汇线段树, 每次连边的时候(这里指的是连单向边的步骤, 双向边要逆过来再做一次), 新建一个节点, 将一棵线段树的对应区间的节点向这个点连边, 再将这个点和另一颗线段树上对应的点连边.
大致思路就是这样. 具体的还有一些连边自行脑补.
考虑如何跑最短路.
由于这里有\(K\)条边的权值可以不用算, 因此我们要跑的是分层图最短路, 意思是在跑Dijkstra的时候不单要记录距离, 还要记录用了多少张免费票.

#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <queue>

namespace Zeonfai
{
    inline int getInt()
    {
        int a = 0, sgn = 1;
        char c;
        while(! isdigit(c = getchar()))
            if(c == '-')
                sgn *= -1;
        while(isdigit(c))
            a = a * 10 + c - '0', c = getchar();
        return a * sgn;
    }
    inline void print(int a)
    {
        if(! a)
            return;
        print(a / 10);
        putchar('0' + a % 10);
    }
    inline void println(int a)
    {
        if(a < 0)
            putchar('-'), a *= -1;
        if(a == 0)
            putchar('0');
        print(a);
        putchar('\n');
    }
}
const int N = (int)5e4, INF = (int)1e9, K = 10;
struct graph
{
    struct node;
    struct edge
    {
        node *v;
        int w;
        inline edge(node *_v, int _w)
        {
            v = _v, w = _w;
        }
    };
    struct node
    {
        int ed, dis[K + 1];
        std::vector<edge> edg;
        inline node()
        {
            ed = 0;
            for(int i = 0; i <= K; ++ i)
                dis[i] = INF;
            edg.clear();
        }
    }nd[N << 2][2], *S;
    inline void addEdge(node *u, node *v, int w)
    {
        u->edg.push_back(edge(v, w));
    }
    void build(int u, int L, int R, int bnd)
    {
        addEdge(&nd[u][1], &nd[u][0], 0);
        if(L == R)
        {
            if(R == bnd)
                nd[u][0].ed = nd[u][1].ed = 1;
            if(L == 1)
                S = &nd[u][0];
            return;
        }
        int mid = L + R >> 1;
        addEdge(&nd[u << 1][0], &nd[u][0], 0), addEdge(&nd[u][1], &nd[u << 1][1], 0);
        build(u << 1, L, mid, bnd);
        addEdge(&nd[u << 1 | 1][0], &nd[u][0], 0), addEdge(&nd[u][1], &nd[u << 1 | 1][1], 0);
        build(u << 1 | 1, mid + 1, R, bnd);
    }
    void build(int n)
    {
        build(1, 1, n, n);
    }
    std::vector<node*> bck[2];
    void get(int u, int curL, int curR, int L, int R, int flg)
    {
        if(curL >= L && curR <= R)
        {
            bck[flg].push_back(&nd[u][flg]);
            return;
        }
        int mid = curL + curR >> 1;
        if(L <= mid)
            get(u << 1, curL, mid, L, R, flg);
        if(R > mid)
            get(u << 1 | 1, mid + 1, curR, L, R, flg);
    }
    void link(int a, int b, int c, int d, int w, int n)
    {
        bck[0].clear(), bck[1].clear();
        get(1, 1, n, a, b, 0), get(1, 1, n, c, d, 1);
        node *u = new node;
        for(auto p : bck[0])
            addEdge(p, u, w);
        for(auto p : bck[1])
            addEdge(u, p, 0);
        u = new node;
        for(auto p : bck[0])
            addEdge(u, p, 0);
        for(auto p : bck[1])
            addEdge(p, u, w);
    }
    struct state
    {
        node *u;
        int k, dis;
        inline state(node *_u, int _k, int _dis)
        {
            u = _u, k = _k, dis = _dis;
        }
        inline int friend operator <(state a, state b)
        {
            return a.dis > b.dis;
        }
    };
    inline int dijkstra(int k)
    {
        std::priority_queue<state> hp;
        S->dis[0] = 0;
        hp.push(state(S, 0, 0));
        for(; ! hp.empty(); hp.pop())
        {
            state stt = hp.top();
            node *u = stt.u;
            if(u->ed)
                return stt.dis;
            for(auto p : u->edg)
            {
                if(stt.dis + p.w < p.v->dis[stt.k])
                    p.v->dis[stt.k] = stt.dis + p.w, hp.push(state(p.v, stt.k, p.v->dis[stt.k]));
                if(stt.k < k && u->dis[stt.k] < p.v->dis[stt.k + 1])
                    p.v->dis[stt.k + 1] = stt.dis, hp.push(state(p.v, stt.k + 1, p.v->dis[stt.k + 1]));
            }
        }
        return INF;
    }
}G;
int main()
{
    
    #ifndef ONLINE_JUDGE
    freopen("YWW.in", "r", stdin);
    #endif
    
    using namespace Zeonfai;
    for(int T = getInt(); T; -- T)
    {
        int n = getInt(), m = getInt(), k = getInt();
        G.build(n);
        for(int i = 0; i < m; ++ i)
        {
            int a = getInt(), b = getInt(), c = getInt(), d = getInt(), w = getInt();
            G.link(a, b, c, d, w, n);
        }
        int ans = G.dijkstra(k);
        if(ans == INF)
            puts("Yww is our red sun!");
        else
            println(ans);
    }
}

转载于:https://www.cnblogs.com/ZeonfaiHo/p/7168252.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值