不难的题。
考虑一个空的点的贡献是一个组合数的形式,即答案为
∑(leftK)(rightK)(upK)(downK)
怎么求呢? 显然先离散,只需考虑离散后的网格上的点。
对于每一行,可以考虑扫过取求两个实点中间空点的贡献,每一段的贡献是
(leftK)(rightK)∑(upK)(downK)
前面两项是不变的,所以我们需要维护每一列当前的形如 (upK)(downK) 的贡献,支持查询区间和。树状数组即可。
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL maxn=200005,MOD=2147483648LL;
struct data{ int x,y; } a[maxn];
vector<int> r[maxn];
int n,K,cntAll[maxn],cntNow[maxn],b1[maxn],b2[maxn];
LL C[maxn][13],ans;
int ID1(int x){ return lower_bound(b1+1,b1+1+b1[0],x)-b1; }
int ID2(int x){ return lower_bound(b2+1,b2+1+b2[0],x)-b2; }
LL bit[maxn];
void Update(int x,LL val){
for(val%=MOD;x<=b2[0];x+=(x&(-x))) (bit[x]+=val)%=MOD;
}
LL Query(int x){
LL res=0;
for(;x;x-=(x&(-x))) (res+=bit[x])%=MOD;
return res;
}
int main(){
freopen("bzoj1227.in","r",stdin);
freopen("bzoj1227.out","w",stdout);
scanf("%*d%*d%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
b1[++b1[0]]=a[i].x; b2[++b2[0]]=a[i].y;
}
scanf("%d",&K);
for(int i=0;i<=100000;i++){
C[i][0]=1; for(int j=1;j<=min(i,K);j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
}
sort(b1+1,b1+1+b1[0]); b1[0]=unique(b1+1,b1+1+b1[0])-(b1+1);
sort(b2+1,b2+1+b2[0]); b2[0]=unique(b2+1,b2+1+b2[0])-(b2+1);
for(int i=1;i<=n;i++){
r[ID1(a[i].x)].push_back(ID2(a[i].y));
cntAll[ID2(a[i].y)]++;
}
for(int i=1;i<=b1[0];i++){
sort(r[i].begin(),r[i].end());
for(int j=0,lst=0,sz=r[i].size();j<sz;j++){
(ans+=(Query(r[i][j]-1)-Query(lst))%MOD*C[j][K]%MOD*C[sz-j][K]%MOD)%=MOD;
lst=r[i][j];
Update(lst,-C[cntAll[lst]-cntNow[lst]][K]*C[cntNow[lst]][K]);
cntNow[lst]++;
Update(lst,C[cntAll[lst]-cntNow[lst]][K]*C[cntNow[lst]][K]);
}
}
printf("%lld\n",(ans+MOD)%MOD);
return 0;
}