読者です 読者をやめる 読者になる 読者になる

Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

JOI春合宿 2009-day1 pyramid

問題文 : 貫きピラミッド

解法 :
自分の地点での大きさを, 次のように, 2方向から伝搬しながら更新してやれば良い.どちらから先に行なっても良い.


f:id:kagamiz:20121227013405p:plain

f:id:kagamiz:20121227013414p:plain

f:id:kagamiz:20121227013429p:plain

この更新が終われば, あとは各マスの和を取るだけである.

O(WH)回の上書きで答えが求まるので, 計算量はO(WH)時間となる. ただし, 2方向 * 4つの隣接するマス で生じる定数 8 に注意.

コード :

#include <cstdio>
#include <algorithm>

using namespace std;

typedef long long lint;

int dx[2][4] = {{-1, -1, 0, 1}, {1, 1, 0, -1}};
int dy[2][4] = {{0, -1, -1, -1}, {0, 1, 1, 1}};

int w, h, n;

int map[3000][3000];

bool ok(int y, int x)
{
	return (0 <= y && y < h && 0 <= x && x < w);
}

int main()
{
	scanf("%d %d %d", &w, &h, &n);
	
	for (int i = 0; i < n; i++){
		int x, y, z;
		scanf("%d %d %d", &x, &y, &z);
		map[y][x] = max(map[y][x], z);
	}
	
	for (int i = 0; i < h; i++){
		for (int j = 0; j < w; j++){
			for (int k = 0; k < 4; k++){
				int ny = i + dy[0][k], nx = j + dx[0][k];
				if (ok(ny, nx)) map[i][j] = max(map[i][j], map[ny][nx] - 1);
			}
		}
	}
	
	for (int i = h - 1; i >= 0; i--){
		for (int j = w - 1; j >= 0; j--){
			for (int k = 0; k < 4; k++){
				int ny = i + dy[1][k], nx = j + dx[1][k];
				if (ok(ny, nx)) map[i][j] = max(map[i][j], map[ny][nx] - 1);
			}
		}
	}
	
	lint ret = 0;
	
	for (int i = 0; i < h; i++){
		for (int j = 0; j < w; j++){
			ret += map[i][j];
		}
	}
	
	printf("%lld\n", ret);
	
	return (0);
}