P11131 题解
Aventurine_stone · · 题解
前言:
我这道题用的 spfa 做的,有人可能会说 spfa 肯定会被后来的 hack 数据卡爆。但是仔细看题会发现,这道题是构造不了菊花图的,那么加上 SLF 优化即可通过此题,当然我又额外加了 LLL 优化,直接跑得飞起。
AC 记录。
1. 题目分析
看到这道题,我首先想到的便是先用 spfa 判一下负环,再在每个网格点上都跑一次 spfa,从而求出最小总权值。但这样时间复杂度肯定爆炸,我们可以反过来想。
2. 题目做法
用 spfa 判负环是不需要的,我们只需要判断网格中相邻的两个值加起来是否小于
因为
最后输出所有点的总权值的最小值即可。
3. 代码
#include<bits/stdc++.h>
#define ll long long
int n,m,q,z;
#define pot(x,y) (x-1)*m+y
using namespace std;
const int N=100010,M=400010;
inline int read()
{
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
c=='-'?f=-1:1,c=getchar();
while(c>='0'&&c<='9')
x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x*f;
}
int head[N],ne[M],e[M],w[M],idx;
inline void add(int x,int y,int z)
{
ne[++idx]=head[x];
head[x]=idx;
e[idx]=y,w[idx]=z;
}
int a[N],t;
ll dist[N],mx[N];
bool vis[N];
deque<int>d;
ll sum,num;
void spfa(int x)
{
for(int i=1;i<=z;i++)
dist[i]=LONG_LONG_MAX,vis[i]=0;
dist[x]=0;
d.push_front(x);
num=1;
while(!d.empty())
{
t=d.front();
d.pop_front();
if(num&&dist[t]>sum/num)//LLL优化
{
d.push_back(t);
continue;
}
sum-=dist[t],num--;
vis[t]=0;
for(int i=head[t];i;i=ne[i])
{
int c=e[i];
ll s=dist[t]+w[i];
if(s<dist[c])
{
if(vis[c])
sum-=dist[c]-s;
dist[c]=s;
if(!vis[c])
{
vis[c]=1;
sum+=s,num++;
if(!d.empty()&&s<=dist[d.front()])//SLF优化
d.push_front(c);
else
d.push_back(c);
}
}
}
}
for(int i=1;i<=z;i++)
mx[i]=max(mx[i],dist[i]+a[i]);
}
inline void end()
{
printf("No");
exit(0);
}
ll mn;
int x,y;
int main()
{
n=read(),m=read(),q=read();
z=n*m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
t=pot(i,j);
a[t]=read();
if(j>1)
{
x=pot(i,j-1);
if(a[x]+a[t]<0)
end();
add(t,x,a[t]);
}
if(i>1)
{
x=pot(i-1,j);
if(a[x]+a[t]<0)
end();
add(t,x,a[t]);
}
if(j<m)
add(t,pot(i,j+1),a[t]);
if(i<n)
add(t,pot(i+1,j),a[t]);
}
}
for(int i=1;i<=z;i++)
mx[i]=-LONG_LONG_MAX;
while(q--)
{
x=read(),y=read();
spfa(pot(x,y));
}
mn=LONG_LONG_MAX;
for(int i=1;i<=z;i++)
mn=min(mn,mx[i]);
printf("%lld",mn);
return 0;
}