如果对于一个点,四个方向分别有u,d,l,r棵常青树,那么这棵树对答案的贡献ans[i]=C(u,k) * C(d,k) + C(l,k) * C(r,k)。
我们可以发现在两棵常青树之间的墓地,它们的C(l,k) * C(r,k)都是一样的,我们只需要求出C(l,k) * C(r,k) * sigma(C(u,k) * C(d,k)),求和想到树状数组来维护。
我们用树状数组来维护当前这一行的C(u,k) * C(d,k),在逐渐向上移动时,我们更新树状数组中的这一个值。从下往上逐渐扫就可以了。
坐标需要离散化以下。
#include<bits/stdc++.h>
#define lowbit(i) (i&(-i))
#define ll long long
#define M 2147483648LL
using namespace std;
int n,m,w,k,nowl;
ll c[100005][11],tree[200005];
ll ans;
int sumx[200005],sumy[200005],now[200005],hash[200005];
struct node {int x,y;} a[100005];
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
inline int find(int x)
{
int l=1,r=2*w;
while (l<=r)
{
int mid=l+r>>1;
if (hash[mid]>x) r=mid-1;
else if (hash[mid]<x) l=mid+1;
else return mid;
}
}
inline void pre_C()
{
c[0][0]=1;
for (int i=1;i<=w;i++)
{
c[i][0]=1;
for (int j=1;j<=min(i,k);j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%M;
}
}
inline void add(int x,int val)
{
for (int i=x;i<=2*w;i+=lowbit(i))
tree[i]=(tree[i]+val)%M;
}
inline ll ask(int x)
{
ll tmp=0;
for (int i=x;i;i-=lowbit(i)) tmp=(tmp+tree[i])%M;
return tmp;
}
inline bool cmp(node a,node b)
{
return a.y==b.y?a.x<b.x:a.y<b.y;
}
int main()
{
n=read(); m=read(); w=read();
for (int i=1;i<=w;i++)
hash[2*i-1]=a[i].x=read(),hash[2*i]=a[i].y=read();
k=read();
pre_C();
sort(hash+1,hash+2*w+1);
for (int i=1;i<=w;i++)
sumx[find(a[i].x)]++,sumy[find(a[i].y)]++;
sort(a+1,a+w+1,cmp);
a[0].y=-1;
for (int i=1;i<=w;i++)
{
if (a[i].y==a[i-1].y)
{
nowl++;
int t1=find(a[i].x),t2=find(a[i-1].x);
ll tmp=ask(t1-1)-ask(t2);
ans=(ans+tmp*c[nowl][k]*c[sumy[find(a[i].y)]-nowl][k])%M;
}
else nowl=0;
int d=find(a[i].x); now[d]++;
add(d,(c[now[d]][k]*c[sumx[d]-now[d]][k]-c[now[d]-1][k]*c[sumx[d]-now[d]+1][k])%M);
}
if (ans<0) ans+=M;
cout << ans;
return 0;
}