zoukankan      html  css  js  c++  java
  • 大陆争霸[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)结界。
    大样例一次过,美滋滋
    考试结束,
    9a2aabb07941471c96828e92da2efb90.jpeg
    (color{red}{Wrong Answerqquad10})
    不过,其实这题也就只需要在最短路算法上小小的改动一下就行了。
    开三个数组(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
    ", ans[n]); 
    	return 0;
    } 
    
  • 相关阅读:
    map 取最大value
    tmux 常用快捷键
    史蒂夫 乔布斯(Steve Jobs)在 Stanford 2005年毕业典礼上的演讲(中英文对照)
    黄聪:WordPress后台添加侧边栏菜单(WP教程add_menu_page)
    黄聪:ASP.NET AJAX入门系列(1) AjaxControlToolkit安装篇
    黄聪:ASP.NET AJAX入门系列(2) Accordion控件
    黄聪:wordpress调用函数大全
    黄聪:使用Wordpress中的wpdb类操作数据库
    黄聪:C#的Microsoft图表控件
    黄聪:如何写出兼容性强的CSS代码,适应各种浏览器:IE、火狐等
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM2.html
Copyright © 2011-2022 走看看