Codeforces 253D Table with Letters - 2

問題

nxmのアルファベット小文字の書かれたグリッドがある。
このグリッドの部分長方形うち、四隅のアルファベットが同じで、含まれている'a'の個数がk個であるようなものの個数を求めよ。

制約条件

n, m≦400
k≦nm

方針

尺取法。


アルファベットを固定する。


行を上から見ていって、同じ行のj番目, k番目の文字が同じだったときに、v[j][k]にiを入れていくような処理の仕方を考える。


すると、j, k番目に同じ文字を見つけたとき、
今までの行のうち、どれに、j, k番目に同じ文字があるかは、
v[j][k]を舐めればわかる。


ここで、v[j][k]ごとに添え字idx[j][k]を記憶しておけば、
尺取法をすることができる。
すなわち、今の行でギリギリ長方形が作れるidx≦i+1行以降でギリギリ長方形が作れるidx
がなりたつので、前の行のときに保存したidxから、比較をはじめればよくって、
全体として(j, k)の組一つにつきO(n)回の比較で、全部の長方形の数を数えられる。


比較には、二次元累積和をあらかじめ計算しておけば一回あたりO(1)の時間しかかからない。


が、これだと最悪ケースでベクターに要素がO(nm^2)個入ってしまってMLE.
なので、ループの順番を少し変えて、
右側の文字を固定してやるようにする。
するとオーダーが変わらずに、メモリ消費量だけ減って通る。

ソースコード

int n, m, K;
char in[401][401];

vi v[400][26];
int idx[400][26], a[401][401];

int main(){
	
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
	scanf("%d%d%d", &n, &m, &K);
	rep(i, n){
		scanf("%s", in[i]);
		rep(j, m) a[i+1][j+1] = a[i+1][j] + a[i][j+1] - a[i][j] + (in[i][j] == 'a');
	}
	
	ll ans = 0;
	
	rep(r, m){
		rep(i, m) rep(j, 26){
			idx[i][j] = 0;
			v[i][j].clear();
		}
		rep(i, n){
			rep(j, r) if(in[i][j] == in[i][r]){
				
				vi &vv = v[j][in[i][j] - 'a'];
				int &ii = idx[j][in[i][j] - 'a'];
				int x = a[i+1][r+1] - a[i+1][j];
				
				while(ii < vv.size() && x - vv[ii] > K) ii++;
				
				ans += vv.size() - ii;
				vv.pb(a[i][r+1] - a[i][j]);
			}
		}
	}
	cout << ans << endl;
	
	return 0;
}