[SDOI2010] 大陆争霸 题解

发布时间 2023-12-24 22:14:10作者: CQWYB
[题目传送门](https://www.luogu.com.cn/problem/P2446)

# 解法
由题可知,一个城市$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$

```cpp
#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;
}
```