大陆争霸[SDOI2010]带限制最短路

Posted ak-dream

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大陆争霸[SDOI2010]带限制最短路相关的知识,希望对你有一定的参考价值。

只要你有无限个自爆机器人,你就能为所欲为 斯普林·布拉泽

【题目描述】

杰森国有 \(N\) 个城市,由 \(M\) 条单向道 路连接。杰森国的首都是城市 \(N\)。你只需摧毁杰森国首都就能获胜。为了尽量减小己方的消耗,你决定使用自爆机器人完成这一任务。唯一的困难是,杰森国的一部分城市有结界保护,不破坏掉结界就无法进入城市。而每个城市的结界都是由分布在其他城市中的一些结界发生器维持的,如果想进入某个城市,你就必须破坏掉维持这个城市结界的所有结界发生器。 现在你有无限多的自爆机器人,一旦进入了某个城市,自爆机器人可以瞬间引爆,破坏一个目标(结界发生器,或是杰森国首都),当然机器人本身也会一起被破坏。你需要知道:击败杰森国所需的最短时间。
【输入格式】
第一行两个正整数 N, M。 接下来 M行,每行三个正整数 \(u_i, v_i ,w_i\),表示有一条从城市\(u_i\)到城市\(v_i\)的单向道路,自爆机器人通过这条道路需要\(w_i\)的时间。之后 \(N\) 行,每行描述一个城市。首先是一个正整数 \(l_i\),代表维持这个城市结界所使用的结界发生器数目。之后\(l_i\)\(1\)~\(N\) 之间的城市编号,表示每个结界发生器的 位置。如果\(l_i = 0\),则说明该城市没有结界保护,保证\(l_1 = 0\)
【输出格式】
仅包含一个正整数 ,击败杰森国所需的最短时间。
---
【思路点拨】
如果没有结界的限制,很明显此题就只需要跑一遍\(Dijkstra\)就过了。
第一眼看到这题就想到拓扑排序。
当时在考场上先写了一个最短路算每个点到源点的最短距离,然后根据结界发生器的拓扑序更新\(ans[i] = max(dist[i], ans[j])\),其中城市\(j\)的结界发生器维持城市\(i\)结界。
大样例一次过,美滋滋
考试结束,
技术图片
\(\colorredWrong\ Answer\qquad10\)
不过,其实这题也就只需要在最短路算法上小小的改动一下就行了。
开三个数组\(dist, tme, ans\)\(dist[i]\)表示源点到点\(i\)的最短距离,\(tme[i]\)表示点\(i\)最早在此时解除结界,\(ans[i]\)则表示城市\(i\)最早能在\(ans[i]\)时被炸飞。
显然,\(ans[i] = max(dist[i], tme[i])\)
而对于\(tme[i]\)\(\forall j \in L[i], tme[i] = max(ans[j])\),其中\(L[i]\)代表维持城市\(i\)结界的城市的集合。
然后直接跑一遍\(Dijkstra\), 不过要在更新完\(L[i]\)中所有的城市后才能将\(i\)入堆。
最后的答案就是\(ans[n]\),时间复杂度\(O(m log n)\)

【代码实现】

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define re register
using namespace std;
typedef long long ll;

ll n, m;
ll head[100005], pre[150005], to[150005], val[150005], len;
ll h2[100005], p2[100005], t2[100005], l2, in[100005], ans[100005], dis[100005], tme[100005];
bool vis[100005];

ll read() 
    ll ret = 0, flag = 1;
    char ch = getchar();
    while (ch > '9' || ch < '0') 
        if (ch == '-') flag = -1;
        ch = getchar(); 
    
    while (ch <= '9' && ch >= '0') 
        ret = ret * 10 + ch - '0';
        ch = getchar();
    
    return ret * flag;


void insert(ll u, ll v, ll w) 
    to[++len] = v, val[len] = w, pre[len] = head[u], head[u] = len;
 

void insert2(ll u, ll v) 
    t2[++l2] = v, p2[l2] = h2[u], h2[u] = l2;
 

void dijkstra() 
    for (re int i = 1; i <= n; i++) dis[i] = 0x7fffffff;
    dis[1] = tme[1] = ans[1] = 0;
    priority_queue< pair<ll, ll> , vector< pair<ll, ll> > , greater< pair<ll, ll> > > q;
    q.push(make_pair(0, 1));
    while (!q.empty()) 
        ll c = q.top().second;
        q.pop();
        if (vis[c]) continue;
        vis[c] = 1;
        for (re ll i = head[c]; i != 0; i = pre[i]) 
            if (ans[c] + val[i] < dis[to[i]]) 
                dis[to[i]] = ans[c] + val[i];   
                if (!in[to[i]]) 
                    ans[to[i]] = max(dis[to[i]], tme[to[i]]);
                    q.push(make_pair(ans[to[i]], to[i]));
                
            
        
        for (re ll i = h2[c]; i != 0; i = p2[i]) 
            if (in[t2[i]]) 
                in[t2[i]]--;
                tme[t2[i]] = max(tme[t2[i]], ans[c]);   
                if (!in[t2[i]]) 
                    ans[t2[i]] = max(dis[t2[i]], tme[t2[i]]);
                    q.push(make_pair(ans[t2[i]], t2[i]));
                
            
        
    


int main() 
    n = read();
    m = read();
    ll u, v, w;
    for (re int i = 1; i <= m; i++) 
        u = read(), v = read(), w = read();
        insert(u, v, w);
    
    for (re int i = 1; i <= n; i++) 
        in[i] = read();
        for (re int j = 1; j <= in[i]; j++) 
            u = read();
            insert2(u, i); 
        
    
    dijkstra();
    printf("%lld\n", ans[n]); 
    return 0;
 

以上是关于大陆争霸[SDOI2010]带限制最短路的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 1922 [Sdoi2010]大陆争霸(最短路变形)

BZOJ1922 SDOI2010 大陆争霸 最短路

BZOJ1922 [Sdoi2010]大陆争霸 最短路

[SDOI2010]大陆争霸

[SDOI2010]大陆争霸

Bzoj1922: [Sdoi2010]大陆争霸