【bzoj1922】 Sdoi2010—大陆争霸

时间:2021-12-17 00:15:26

http://www.lydsy.com/JudgeOnline/problem.php?id=1922 (题目链接)

题意

  一张无向图,每个节点被k个节点保护,想要走到一个节点当且仅当它不被保护。你可以从1号节点放出无限个炸弹去炸毁节点,问最少需要多久才可以炸毁n号节点。

Solution

  昨天考试题。

  一看就是最短路然后带一些特殊处理,怎么特殊处理呢?我们使用Dijkstra来做这道题,因为SPFA可以重复入队,所以是错误的。每次出堆的点,将它保护的节点的“保护度数”减1并更新被保护节点的dis(取max),如果某个节点的度数已经被减成了0,并且它的dis不是inf,也就是说已经有一个炸弹在这个节点蓄势待发了,我们将这个节点加入堆中。做完减少度数操作后,就是正常的Dijkstra的入堆操作了,记得只有“保护度数”为0的点才能入堆。

细节

  邻接表的数组开成了节点个数的大小→_→

代码

// bzoj1922
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#define LL long long
#define inf 1e18
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; const int maxn=5010,maxm=100010;
struct edge {int to,next,w;}e[maxm<<1];
struct data {
int num;LL w;
friend bool operator < (const data a,const data b) {
return a.w>b.w;
}
};
LL dis[maxn];
int head[maxn],vis[maxn],r[maxn];
int cnt,n,m;
vector<int> v[maxn]; void link(int u,int v,int w) {
e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].w=w;
}
void Dijkstra() {
for (int i=1;i<=n;i++) dis[i]=inf;
dis[1]=0;
priority_queue<data> q;
q.push((data){1,0});
while (!q.empty() && !vis[n]) {
data x=q.top();q.pop();
if (vis[x.num]) continue;
vis[x.num]=1;
for (int i=0;i<v[x.num].size();i++) {
r[v[x.num][i]]--;
if (!r[v[x.num][i]] && dis[v[x.num][i]]!=inf)
q.push((data){v[x.num][i],max(dis[v[x.num][i]],x.w)});
dis[v[x.num][i]]=max(dis[v[x.num][i]],x.w);
}
for (int i=head[x.num];i;i=e[i].next) {
if (!vis[e[i].to] && dis[e[i].to]>x.w+e[i].w) {
dis[e[i].to]=x.w+e[i].w;
if (!r[e[i].to]) q.push((data){e[i].to,dis[e[i].to]});
}
}
}
}
int main() {
scanf("%d%d",&n,&m);
for (int uu,vv,ww,i=1;i<=m;i++) {
scanf("%d%d%d",&uu,&vv,&ww);
link(uu,vv,ww);
}
for (int i=1;i<=n;i++) {
scanf("%d",&r[i]);
for (int x,j=1;j<=r[i];j++) {
scanf("%d",&x);
v[x].push_back(i);
}
}
Dijkstra();
printf("%lld",dis[n]);
return 0;
}