[SDOI2010] 大陆争霸

发布时间 2023-12-25 19:02:04作者: CQWDX

[SDOI2010] 大陆争霸

屁话真多。

第一眼看上去好像是最短路加了个强制拓扑。

也就是说当结界还没被破坏的时候,已经到达的机器人只能干等着。

在 dijkstra 中,机器人所在的点可以更新最短路。但拓扑图上该点的入度不为 \(0\),即结界产生器没有被全部破坏时,不能入队。

当炸掉一个结界产生器的时候,可以将其在拓扑图上连接的所有点的入度减一。

而当拓扑图上的一个点入度为 \(0\),即结界产生器全部被破坏的时候,便可以入队。

不用考虑当前是否有机器人在该点等待(直接 q.push(v, dis[v] = inf))。即使没有,由于题目保证有解,所以若该点在 \(1\to n\) 的最短路上一定可达。之后也会有更优决策来替代它。

#include <bits/stdc++.h>
typedef long long ll;
const int maxn = 3e3 + 20, maxm = 7e4 + 20;
ll dis[maxn];
int mk[maxn];
struct Graph {
	struct edge {
		int u, v, w, next;
	} e[maxm];
	int head[maxn], idx;
	int inDeg[maxn];
	void add(int x, int y, int z) {
		inDeg[y]++;
		idx++; e[idx].u = x, e[idx].v = y, e[idx].w = z;
		e[idx].next = head[x], head[x] = idx;
	}
	Graph() {
		memset(head, 0, sizeof head);
		idx = 0;
	}
};
struct point {
	int u; ll dis;
	point(int a, ll b) {u = a, dis = b;}
	friend bool operator < (point a, point b) {
		return a.dis > b.dis;
	}
};
void dijkstra(int st, int end, Graph &G, Graph &tG) {
	memset(dis, 0x3f, sizeof dis);
	memset(mk, 0, sizeof mk);
	std::priority_queue <point> q; 
	q.push(point(st, 0)); dis[st] = 0;
	while(!q.empty()) {
		int u = q.top().u; q.pop();
		if(u == end) return;
		if(mk[u]) continue;
		mk[u] = 1;
		for(int i = tG.head[u]; i; i = tG.e[i].next) {
			int v = tG.e[i].v; tG.inDeg[v]--;
			dis[v] = std::max(dis[u], dis[v]);
			if(tG.inDeg[v] == 0) q.push(point(v, dis[v]));
		}
		for(int i = G.head[u]; i; i = G.e[i].next) {
			int v = G.e[i].v, w = G.e[i].w;
			if(dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				if(tG.inDeg[v] == 0) q.push(point(v, dis[v]));
			}
		}
	}
}
int n, m;
Graph G, topoG;
void solve() {
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= m; i++) {
		int u, v, w;
		scanf("%d %d %d", &u, &v, &w);
		G.add(u, v, w);
	}
	for(int i = 1; i <= n; i++) {
		int num; scanf("%d", &num);
		for(int j = 1; j <= num; j++) {
			int x; scanf("%d", &x);
			topoG.add(x, i, 0);
		} 
	}
	dijkstra(1, n, G, topoG);
	printf("%lld", dis[n]);
}