kmjp's blog

競技プログラミング参加記です

2012 WUPC2 : I - その味は甘くて

新年になりましたが、ようやくWUPC2のIの解説が出たのでチャレンジ。
http://wupc2nd.contest.atcoder.jp/tasks/wupc_09


ハニカム状のマス目に、指示に従って整数値(糖度)を増していき、糖度の最大値を求める。
いわゆるimos法の典型例。

部分点50は自前でもできた。
部分点50では、type=1(六角形状のマスに糖度を1追加する)だけ。
指示がN通り、六角形の最大半径がSとする。
なので、左から右にimos法を実行すると1つ六角形の糖度を追加するのにSかかるので、糖度の追加にO(NS)、最後の足し合わせがO(S^2)程度となる。

以下部分点50の回答例。
六角形の上半分と下半分の左端で+1、右端で-1して、最後に累積和を取る。

int N;
int types[3][2002][2002];


void solve() {
	int t,x,y,z,i,j,rr,TM;
	int tx,ty,dy;
	ll res,p,tp;
	
	N=GETi();
	ZERO(types);
	FOR(i,N) {
		t=GETi();
		x=GETi()+1000;
		y=GETi()+1000;
		z=GETi();
		if(t==1) {
			for(dy=-(z-1);dy<=(z-1);dy++) {
				ty=y+dy;
				if(dy>=0) {
					types[0][ty][x-(z-1)]++;
					types[0][ty][x+(z-1)-dy+1]--;
				}
				else {
					types[0][ty][x-(z-1)-dy]++;
					types[0][ty][x+z]--;
				}
			}
		}
		else return;
	}
	
	res=0;
	ll ti1;
	FOR(y,2001) {
		ti1=0;
		FOR(x,2001) {
			ti1+=types[0][y][x];
			res=max(res,ti1);
		}
	}
	_P("%lld\n",res);
	return;
}

続いて満点回答。
先と同じtype=1の高速化を試みる。
1方向のimos法だと、1つ六角形を追加するのにO(S)かかる。
ただ、解説の様に3方向のimos法を取ると、O(1)で処理できるので、N個の指示に対しO(N)または足しあわせのO(S^2)の処理で間にあう。

解説を見ると、type=2・type=3の指示をO(1)で処理する方法が書いてあるが、1処理O(S)で良ければこれより簡単に処理できる。
実際先の1方向imos法では1処理O(S)、全部でO(NS)で終わっているので、簡単な方でやってみる。
type=2・3の指示は、ピラミッド状に六角形の中心ほど高い糖度が加わるが、以下のようにtype=1の和で考えられる。

  • type=2のS=3の六角形の糖度=\sum_i^s(S=iの大きさの六角形の範囲に糖度1を加算)
  • type=2のS=3の六角形の糖度=\sum_i^s(S=iの大きさの六角形の範囲に糖度(S*2-i)を加算)

1つのtype=1の六角形の糖度加算がO(1)なので、これらはO(S)で処理でき、N個の指示もO(NS)で処理できる。

他の人の回答みたら、解説よりもこちらの方法を取る人が多かったね。
解説の方法は1指示O(1)、こちらはO(S)なので、時間制限がシビアだと解説の方法を取らないとだめだけどね。
でもこちらの方が解き方としてはわかりやすいと思う。

int N;
ll types[2][2002][2002];

void solve() {
	int t,x,y,z,i,j,r,k,TM;
	int tx,ty,dy;
	ll res,p,tp;
	
	N=GETi();
	ZERO(types);
	FOR(i,N) {
		t=GETi();
		x=GETi()+1000;
		y=GETi()+1000;
		r=GETi();
		for(j=1;r>0;r--) {
			types[0][x-r+1][y]+=j;		types[0][x-r+2][y]-=j;
			types[0][x+r][y]+=j;			types[0][x+r+1][y]-=j;
			types[0][x+1][y+r]+=j;		types[0][x+1][y+r-1]-=j;
			types[0][x-r+2][y+r-1]+=j;	types[0][x-r+1][y+r]-=j;
			types[0][x+r+1][y-r]+=j;		types[0][x+r][y-r+1]-=j;
			types[0][x+1][y-r+1]+=j;		types[0][x+1][y-r]-=j;
			if(t==1) break;
			if(t==3) j+=2;
		}
	}
	
	
	// 右上に累積
	FOR(x,2001) {
		p=0;
		FOR(y,2001) types[1][x][y]=(p+=types[0][x][y]);
	}
	
	// 右下に累積
	FOR(r,4001) {
		p=0;
		if(r<=2000) for(x=0,y=r;y>=0;y--,x++) types[0][x][y]=(p+=types[1][x][y]);
		else for(x=r-2000,y=2000;x<=2000;y--,x++) types[0][x][y]=(p+=types[1][x][y]);
	}
	//右に累積
	res=0;
	FOR(y,2001) {
		p=0;
		FOR(x,2001) res=max(res,types[1][x][y]=(p+=types[0][x][y]));
	}
	
	_P("%lld\n",res);
	return;
}
||>

*まとめ

複数方向に実行するimos法、まだ習得できてないので練習しないとな。