Problem:
城壁 (Rampart)

歴史学者である JOI 教授は，かつて存在した IOI 王国について研究している．

過去の調査によると，IOI 王国は縦 H 行，横 W 列のマスに区切られた長方形の形をしていた．IOI 王国の首都は，防衛のために城壁で囲われていた．

IOI 王国の首都を囲う城壁は次のような形をしている．城壁には大きさと呼ばれる値が定まっている．大きさ s (s ≥ 3) の城壁とは，s × s の正方形の領域から外周以外の (s − 2) × (s − 2) の正方形の領域を除いたものである．

調査によると，首都を囲う城壁の大きさは L 以上であった．また，IOI 王国のいくつかのマスには城壁が存在しなかったことがわかっている．

JOI 教授は，さらなる研究のために，城壁としてありうるものが何通りあるかを知りたい．

課題

IOI 王国の大きさと，城壁の大きさの最小値，城壁が存在しなかったことが分かっているマスの情報が与えられたとき，城壁としてありうるものは何通りあるかを求めるプログラムを作成せよ．

入力

標準入力から以下のデータを読み込め．

1 行目には，整数 H, W, L, P が空白を区切りとして書かれている．これは，IOI 王国は縦 H 行，横 W 列のマスに区切られた長方形の形をしており，城壁の大きさは L 以上であり，城壁が存在しなかったことがわかっているマスが P マス存在することを表す．

続く P 行のうちの i 行目 (1 ≤ i ≤ P) には，整数 Ai, Bi が空白を区切りとして書かれている．これは，IOI 王国の上から Ai 行目，左から Bi 列目のマスには城壁が存在しなかったことがわかっていることを表す．

出力

標準出力に，城壁としてありうるものは何通りあるかを表す整数を 1 行で出力せよ.

制限

すべての入力データは以下の条件を満たす．

1 ≤ H ≤ 4 000．

1 ≤ W ≤ 4 000．

3 ≤ L ≤ H かつ 3 ≤ L ≤ W．

0 ≤ P ≤ 100 000．

1 ≤ Ai ≤ H (1 ≤ i ≤ P)．

1 ≤ Bi ≤ W (1 ≤ i ≤ P)．

(Ai, Bi) ≠ (Aj, Bj) (1 ≤ i < j ≤ P)．

入出力例

入力例 1

5 5 3 2
2 2
4 3

出力例 1

4

この入力例の場合，城壁としてありうるものは以下の 4 通りが考えられる．ただし，× で示したマスは城壁が存在しなかったことがわかっているマスである．

入力例 2

7 8 4 3
2 2
3 7
6 5

出力例 2

13

入力例 3

4000 4000 1234 4
1161 3028
596 1892
3731 2606
702 1530

出力例 3

7050792912

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C++ (judge verdict: Memory Limit Exceeded)

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <iostream>
#include <string>
#include <queue>
#include <map>
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define sc second
typedef long long ll;
int n,m,lim,p;
bool f[4005][4005];
int le[4005][4005];
int ri[4005][4005];
int d[4005][4005];
int u[4005][4005];
vector<int>seg[8200];
void update(int a,int b)
{
	a+=4095;
	seg[a].pb(b);
}
void all()
{
	for(int i=4094;i>=0;i--)
	{
		if(seg[i*2+1].empty() && seg[i*2+2].empty()) continue;
		else if(seg[i*2+1].empty()) seg[i] = seg[i*2+2];
		else if(seg[i*2+2].empty()) seg[i] = seg[i*2+1];
		else
		{
			seg[i].resize(seg[i*2+1].size()+seg[i*2+2].size());
			merge(seg[i*2+1].begin(),seg[i*2+1].end(),seg[i*2+2].begin(),seg[i*2+2].end(),seg[i].begin());
		}
	}
}
int query(int a,int b,int k,int lb,int ub,int x)
{
	if(ub < a || b < lb) return 0;
	if(a <= lb && ub <= b)
	{
		int z = -1;
		if(!seg[k].empty())
		{
			z = lower_bound(seg[k].begin(),seg[k].end(),x)-seg[k].begin();
			return (seg[k].size()-z);
		}
		else return 0;
	}
	int lk = query(a,b,k*2+1,lb,(lb+ub)/2,x);
	int uk = query(a,b,k*2+2,(lb+ub)/2+1,ub,x);
	return lk+uk;
}
void init()
{
	for(int i=0;i<8200;i++) if(!seg[i].empty()) seg[i].clear();
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&lim,&p);
	for(int i=0;i<p;i++)
	{
		int x,y; scanf("%d%d",&x,&y);
		f[x][y] = true;
	}
	for(int i=1;i<=n;i++)
	{
		int l = 1;
		for(int j=1;j<=m;j++)
		{
			if(f[i][j])
			{
				while(l!=j)
				{
					ri[i][l] = j-l;
					l++;
				}
			}
		}
		while(l<=m)
		{
			ri[i][l] = m-l+1;
			l++;
		}
	}
	for(int i=1;i<=n;i++)
	{
		int l = m;
		for(int j=m;j>=1;j--)
		{
			if(f[i][j])
			{
				while(l!=j)
				{
					le[i][l] = l-j;
					l--;
				}
			}
		}
		while(l>=1)
		{
			le[i][l] = l;
			l--;
		}
	}
	for(int i=1;i<=m;i++)
	{
		int l = 1;
		for(int j=1;j<=n;j++)
		{
			if(f[j][i])
			{
				while(l!=j)
				{
					d[l][i] = j-l;
					l++;
				}
			}
		}
		while(l<=n)
		{
			d[l][i] = n-l+1;
			l++;
		}
	}
	for(int i=1;i<=m;i++)
	{
		int l = n;
		for(int j=n;j>=1;j--)
		{
			if(f[j][i])
			{
				while(l!=j)
				{
					u[l][i] = l-j;
					l--;
				}
			}
		}
		while(l>=1)
		{
			u[l][i] = l;
			l--;
		}
	}

	ll res = 0;
	for(int i=-n+1;i<=0;i++)
	{
		if(min(m-i,n)+i < lim) continue;
		bool uu = 0;
		for(int j=1-i;j<=min(m-i,n);j++)
		{
			if(f[j][i+j]) continue;
			if(min(d[j][i+j],ri[j][i+j]) >= lim) uu = 1;
			update(j-(1-i),min(le[j][i+j],u[j][i+j])-(j-(1-i)));
		}
		if(!uu){ init(); continue;}
		all();
		for(int j=1-i;j<=min(m-i,n);j++)
		{
			if(min(d[j][i+j],ri[j][i+j]) < lim) continue;
			if(f[j][i+j]) continue;
			int k = j-(1-i);
			int h = min(d[j][i+j],ri[j][i+j]);
			res += query(k+lim-1,k+h-1,0,0,4095,-k+1);
			//cout << query(k+lim-1,k+h-1,0,0,4095,-k+1) << " " << j << " " << i+j << endl;
		}
		init();
	}
	for(int i=1;i<=m-1;i++)
	{
		if(min(m-i,n) < lim) continue;
		bool uu = 0;
		for(int j=1;j<=min(m-i,n);j++)
		{
			if(f[j][i+j]) continue;
			if(min(d[j][i+j],ri[j][i+j]) >= lim) uu = 1;
			update(j-1,min(le[j][i+j],u[j][i+j])-(j-1));
		}
		if(!uu){ init(); continue;}
		all();
		for(int j=1;j<=min(m-i,n);j++)
		{
			if(min(d[j][i+j],ri[j][i+j]) < lim) continue;
			if(f[j][i+j]) continue;
			int k = j-1;
			int h = min(d[j][i+j],ri[j][i+j]);
			res += query(k+lim-1,k+h-1,0,0,4095,-k+1);
			//cout << query(k+lim-1,k+h-1,0,0,4095,-k+1) << " " << j << " " << i+j << endl;
		}
		init();
	}
	printf("%lld\n",res);
}