CCF 201703-5 引水入城

问题描述
  MF城建立在一片高原上。由于城市唯一的水源是位于河谷地带的湖中,人们在坡地上修筑了一片网格状的抽水水管,以将湖水抽入城市。
  如下图所示:
这里写图片描述
  这片管网由 n 行 m 列节点(红色,图中 n = 5,m = 6),横向管道(紫色)和纵向管道(橙色)构成。
  行和列分别用 1 到 n 的整数和 1 到 m 的整数表示。第 1 行的任何一个节点均可以抽取湖水,湖水到达第 n 行的任何一个节点即算作引入了城市。
  除第一行和最后一行外,横向相邻或纵向相邻的两个节点之间一定有一段管道,每一段管道都有各自的最大的抽水速率,并需要根据情况选择抽水还是放水。对于纵向的管道(橙色),允许从上方向下方抽水或从下方向上方放水;如果从图中的上方向下方抽水,那么单位时间内能通过的水量不能超过管道的最大速率;如果从下方向上方放水,因为下方海拔较高,因此可以允许有任意大的水量。对于横向的管道(紫色),允许从左向右或从右向左抽水,不允许放水,两种情况下单位时间流过的水量都不能超过管道的最大速率。
  现在MF城市的水务负责人想知道,在已知每个管道单位时间容量的情况下,MF城每单位时间最多可以引入多少的湖水。
  
输入格式
  由于输入规模较大,我们采用伪随机生成的方式生成数据。
  每组数据仅一行包含 6 个非负整数 n, m, A, B, Q, X0。其中,n 和 m 如前文所述,表示管网的大小,保证 2 ≤ n, m ≤ 5000;保证1 ≤ A, B, Q, X0 ≤ 109。
  A, B, Q, X0 是数据生成的参数,我们用如下的方式定义一个数列 { Xi }:Xi+1 = ( AXi + B) mod Q, (i ≥ 0)
  我们将数列的第 1 项到第 (n-1)m 项作为纵向管道的单位时间容量,其中 X(s-1)m+t 表示第 s 行第 t 列的节点到第 s+1 行第 t 列管道单位时间的容量;将数列的第 (n-1)m+1 项到第 (n-1)m+(n-2)(m-1) 项(即接下来的 (n-2)(m-1) 项)作为横向管道的单位时间容量,其中 X(n-1)m+(s-2)(m-1)+t 表示第 s 行第 t 列的节点到第 s 行第 t+1 列管道单位时间的容量。
  
输出格式
  输出一行一个整数,表示MF城每单位时间可以引入的水量。
  注意计算过程中有些参数可能超过32位整型表示的最大值,请注意使用64位整型存储相应数据。
  
样例输入

3 3 10 3 19 7

样例输出

38

样例说明
  使用参数得到数列 { Xi }={ 7, 16, 11, 18, 12, 9, 17, 2, 4, … },按照输入格式可以得到每个管道的最大抽水量如下图所示:这里写图片描述
  在标准答案中,单位时间可以引水 38 单位。所有纵向管道均向下抽水即可,不需要横向管道抽水,也不需要向上放水。
  
样例输入

2 5 595829232 749238243 603779819 532737791

样例输出

1029036148

样例输入

5 2 634932890 335818535 550589587 977780683

样例输出

192923706

样例输入

5 5 695192542 779962396 647834146 157661239

样例输出

1449991168

评测用例规模与约定
  共有10组评测数据,每组数据的参数规模如下所示:
这里写图片描述

解题思路
  本题参考了网上的题解,不过最后只有60分,欢迎大家在下面给出AC代码。


#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
#define N 5010
int n, m;
int a[N][N], b[N][N];
int read() {
	int A, B, Q, XI;
	scanf("%d%d%d%d%d%d", &n, &m, &A, &B, &Q, &XI);
	//a
	for (int i = 1; i<n; i++)
		for (int j = 1; j <= m; j++) {
			XI = (1ll * A*XI + B) % Q;
			a[i][j] = XI;
		}
	//b
	for (int i = 2; i <= n - 1; i++)
		for (int j = 1; j<m; j++) {
			XI = (1ll * A*XI + B) % Q;
			b[i][j] = XI;
		}
	return 0;
}
int ed;
int ed_x[N], ed_y[N];
int ed_w[N];
int getEdge(int x, int y) { //从(x,y)出发点连边
	ed = 0;
	if (x == 0) {    //起始或终点
		if (y == 0) { //s
			for (int i = 1; i<n; i++) {
				ed++;
				ed_x[ed] = i;
				ed_y[ed] = 1;
				ed_w[ed] = a[i][1];
			}
		}
	}
	else {  //格子节点
			//右
		ed++;
		if (y == m - 1) {//通向汇点t
			ed_x[ed] = 0; ed_y[ed] = 1; ed_w[ed] = a[x][m];
		}
		else {
			ed_x[ed] = x; ed_y[ed] = y + 1; ed_w[ed] = a[x][y + 1];
		}
		//左
		if (y>1) {
			ed++;
			ed_x[ed] = x; ed_y[ed] = y - 1; ed_w[ed] = a[x][y];
		}
		//下
		if (x<n - 1) {
			ed++;
			ed_x[ed] = x + 1; ed_y[ed] = y; ed_w[ed] = b[x + 1][y];
		}
		//上
		if (x>1) {
			ed++;
			ed_x[ed] = x - 1; ed_y[ed] = y; ed_w[ed] = b[x][y];
		}
	}
	return 0;
}
void showEd() {
	for (int i = 1; i <= ed; i++) {
		printf("(%d,%d) %d\n", ed_x[i], ed_y[i], ed_w[i]);
	}
}
bool done[N][N];
long long d[N][N];
struct NODE {
	int x, y;
	long long d;
	NODE(int x = 0, int y = 0, long long d = 0) :x(x), y(y), d(d) {}
	bool operator <(const NODE& rhs)const {
		return d>rhs.d;
	}
};
#define INF (9187201950435737471ll)
priority_queue<NODE> Q;
long long solve() {
	memset(d, 127, sizeof(d));
	d[0][0] = 0;
	memset(done, 0, sizeof(done));
	Q.push(NODE(0, 0, 0));
	while (!Q.empty()) {
		NODE nd = Q.top(); Q.pop();
		if (nd.x == 0 && nd.y == 1) return nd.d;
		if (done[nd.x][nd.y])continue;
		done[nd.x][nd.y] = true;
		d[nd.x][nd.y] = nd.d;
		getEdge(nd.x, nd.y);
		for (int i = 1; i <= ed; i++) {
			int x = ed_x[i];
			int y = ed_y[i];
			int w = ed_w[i];
			//printf("(%d,%d)->(%d,%d) %d\n",nd.x,nd.y,x,y,w);
			if (d[x][y]>d[nd.x][nd.y] + w) {
				d[x][y] = d[nd.x][nd.y] + w;
				Q.push(NODE(x, y, d[x][y]));
			}
		}
	}
	return 0;
}
int main() {
	read();
	/*for (int i=1;i<n;i++,putchar('\n'))
	for (int j=1;j<=m;j++)printf("%d ",a[i][j]);
	getEdge(0,0);
	showEd();*/
	cout << solve() << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值