题目描述
有一个(n)行(m)列的网格图。
(S)到第一行的每一个点都有一条单向边,容量为(infty)。
最后一行的每个点到(T)都有一条单向边,容量为(infty)。
同一行中相邻的两个节点之间有一条无向边,((x,y))和((x,y+1))之间的无向边的容量为(a_{x,y})。
同一列中相邻的两个节点之间有两条有向边,((x,y))到((x+1,y))这条有向边的容量为(b_{x,y}),((x+1,y))到((x,y))这条有向边容量为(infty)。
求(S)到(T)的最大流。
特别的,(forall i,a_{1,i}=a_{n,i}=0)
(n imes mleq 25000000)
题解
显然这是一个网络流。
直接跑网络流会TLE。
观察到这个图是一个平面图,可以把平面图网络流转化为对偶图最短路。
怎么转化呢?
首先你要会无向边的平面图网络流(可以百度/google)。
有向边的连边方法和无向边的类似。
对于一条有向边(x ightarrow y),容量为(z)的有向边(网络流最后都是在有向边上面跑的),从(x ightarrow y)这条有向边的左边对应的这个区域连一条边到右边的这个区域,权值为(z)。
最后跑一次最短路就行了。
这道题中从下往上的边的容量为(infty),所以对偶图中从左往右的边的权值为(infty),也就是说最短路的每一步只会向上/下/左走,这就可以DP了。
设(f_{i,j})为走到((x,y))右下方那个区域的最短路
[f_{i,j}=min(f_{i-1,j}+a_{i,j},f_{i+1,j}+a_{i+1,j},f_{i,j+1}+b_{i+1,j})
]
时间复杂度:(O(nm))
我的代码中把左右反过来了
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
if(a>b)
swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
char str[100];
sprintf(str,"%s.in",s);
freopen(str,"r",stdin);
sprintf(str,"%s.out",s);
freopen(str,"w",stdout);
#endif
}
int rd()
{
int s=0,c;
while((c=getchar())<'0'||c>'9');
do
{
s=s*10+c-'0';
}
while((c=getchar())>='0'&&c<='9');
return s;
}
void put(int x)
{
if(!x)
{
putchar('0');
return;
}
static int c[20];
int t=0;
while(x)
{
c[++t]=x%10;
x/=10;
}
while(t)
putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
if(b<a)
{
a=b;
return 1;
}
return 0;
}
int upmax(int &a,int b)
{
if(b>a)
{
a=b;
return 1;
}
return 0;
}
int *a;
int n,m;
int A,B,Q;
int down(int x,int y)
{
return a[(x-1)*m+y];
}
int right(int x,int y)
{
return a[(n-1)*m+(x-2)*(m-1)+y];
}
ll *f;
int main()
{
open("c");
scanf("%d%d",&n,&m);
a=new int[(n-1)*m+(n-2)*(m-1)+1];
scanf("%d%d%d%d",&A,&B,&Q,&a[0]);
for(int i=1;i<=(n-1)*m+(n-2)*(m-1);i++)
a[i]=((ll)a[i-1]*A+B)%Q;
f=new ll[n];
for(int i=1;i<n;i++)
f[i]=0;
for(int i=1;i<m;i++)
{
for(int j=1;j<n;j++)
f[j]+=down(j,i);
for(int j=2;j<n;j++)
f[j]=min(f[j],f[j-1]+right(j,i));
for(int j=n-2;j>=1;j--)
f[j]=min(f[j],f[j+1]+right(j+1,i));
}
ll ans=0x7fffffffffffffffll;
for(int i=1;i<n;i++)
ans=min(ans,f[i]+down(i,m));
printf("%lld
",ans);
return 0;
}