[SDOI2010] 大陆争霸 题解

题目传送门

解法

由题可知,一个城市\(u\)保护城市\(v\),所以建一条边\(u \to v\)表示城市\(u\)保护城市\(v\),因为题目说保证有解,所以建的图一定是一个有向无环图\(DAG\) 。再在此基础上求出最短路径。

具体过程为设\(dis_u\)表示实际到达(攻破)\(u\)的最短时间,\(arrive_u\)表示到达\(u\)的时间(注意\(arrive_u \neq dis_u\),而是\(arrive_u \le dis_u\))。然后考虑怎么转移,我们发现\(arrive_u\)只会影响一个值就是\(dis_u\),而\(dis_u\)能影响其他节点的\(arrive\)\(dis\)值。

所以先考虑\(arrive_u\)的转移:

  • \(arrive_u=\min(dis_v,arrive_u)\),其中\(v\)为与\(u\)相邻的节点。显然\(arrive_v\)不能更新\(dis_u\),因为\(arrive_v\)表示只是到达了\(v\)点,但可能还没有进入\(v\)点,所以不能这样更新。

然后再来考虑\(dis_u\)的转移:

  • \(dis_u=\max(arrive_u,dis_v)\)\(v\)表示保护\(u\)的节点编号。如果\(arrive_u \le dis_v\)表明到达\(u\)点可能还没有进入的最早时间比攻破\(v\)点的时间早,那么显然此时\(dis_u\)应该由\(dis_v\)决定。如果\(arrive_u \ge dis_v\)表明到达\(u\)点可能还没有进入的最早时间比攻破\(v\)点的时间晚,而因为此时保护\(u\)点的节点都被攻破了,所以由\(arrive_u\)来决定,即\(dis_u=arrive_u\)

转移过想出来了,于是现在考虑用怎样的顺序能正确更新\(dis_u\)\(arrive_u\)。我们发现上述过程与\(dijkstra\)算法的过程很相似,如果我们也每次取出\(dis\)值最小的点来进行更新就行。(为什么可以直接取出\(dis\)值最小的点进行更新?因为类似于\(djikstra\)的证明一定不存在另外的路径使得\(dis\)值再变小。)于是我们就可以用\(dijkstra+topo\)来实现啦。

\(Code\)

#include<bits/stdc++.h>
using namespace std;
const int N=3050,M=70050;
long long dis[N],arrive[N],into[N];
int n,m;
int head[N],to[M],w[M],nxt[M],cnt,in[N];
bool vis[N];
vector<int> g[N];
void add(int u,int v,int f)
{
	to[++cnt]=v;
	nxt[cnt]=head[u];
	w[cnt]=f;
	head[u]=cnt;
}
struct node{
	long long val;int pos;
	bool operator >(const node &x)const{
		return val>x.val;
	}
};
void dij(int s)
{
	for(int i=1;i<=n;i++)dis[i]=arrive[i]=1e18;
	dis[s]=into[s]=arrive[s]=0;
	priority_queue<node,vector<node>,greater<node> >q;
	q.push(node{0,s});
	while(!q.empty())
	{
		node t=q.top();q.pop();
		if(vis[t.pos])continue;
//		cout<<t.pos<<endl;
		vis[t.pos]=1;
		for(int i=head[t.pos];i;i=nxt[i])
		{
			int v=to[i];
			if(arrive[v]>dis[t.pos]+w[i])
			{
				arrive[v]=dis[t.pos]+w[i];
				if(!in[v])
				{
					dis[v]=max(into[v],arrive[v]);
					q.push(node{dis[v],v});
//					cout<<v<<endl;
				}
			}
		}
		for(auto v:g[t.pos])
		{
			in[v]--;
			into[v]=max(into[v],dis[t.pos]);
			if(!in[v])
			{
				dis[v]=max(arrive[v],into[v]);
				q.push(node{dis[v],v});
			}
		}
	}
	return;
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1,u,v,w;i<=m;i++)
	{
		scanf("%d %d %d",&u,&v,&w);
		add(u,v,w);
	}
	for(int i=1;i<=n;i++)
	{
		int x,v;
		scanf("%d",&x);
		while(x--)
		{
			scanf("%d",&v);
			g[v].push_back(i);
			in[i]++;
		}
	}
	dij(1);
	printf("%lld\n",dis[n]);
	return 0;
}
posted @ 2023-12-24 22:09  CQWYB  阅读(40)  评论(0编辑  收藏  举报