这个题是今天上午模拟赛做的,考场上代码最后时间紧写得巨丑,所以改完以后还是巨丑
这是考场上写的,然而数学实在太渣,不知道在模数下不能做除法,组合数部分写ci了
首先都知道组合数是必须要处理的,然而这个题直接处理阶乘做组合数公式是不行的,因为模数下不能做除法。然后既然都要处理出来,又不需要O(1),可以用杨辉三角,如果普通的杨辉三角,可以做到3000*3000(其实组合数的处理决定了大部分分数)
首先可以看出肯定要离散化坐标,然后也需要用扫描线,然后对于每一个横坐标,枚举在这一横坐标上的从下向上第K个点和从上向下第K个点中间所有的墓地,算出它们的权值,然后相加,这个做法亲测80
这12分是给组合数处理的,看题会发现K惊奇地小,而且所有组合数是从x个里面找K个,所以组合数只需要枚举1到W*1到K,这样可以处理所有情况下的组合数了
前面暴力的做法已经说完了,如果是赛场上剩下的8分其实可以选择性放弃了,然而在刷题的时候还是尽可能优化。可以发现在同一横坐标两个点之间,上下的组合数乘积是一定的,然后我们只要能快速求出两个节点之间,所有满足条件墓地左右组合数乘积的和就可以了,这个可以用一个数据结构维护(我写的线段树大概最慢0.9s,luogu可能是为了必须用树状数组时间开到0.8),然后中间还可以有一些优化,例如如果一个纵坐标上所有点加起来都不到2*K,这个纵坐标上所有的点就不用加入数据结构(小优化线段树还是过不了最后一个点)
(后期补一下,本来以为过不去是数据结构的问题,结果建哥告诉我直接用按坐标排下序就可以了,没必要用vector,我想不至于啊不就是把每个数扔到vector里面又取出来一遍吗。然而改了一下快到飞起,vector常数杀手)
#include
#include
#include
#include
#include
#define LL long long
#define MAXN (100010)
#define MOD (2147483648)
#define ls ((rt<<1)+1)
#define rs ((rt<<1)+2)
#define mid ((l+r)>>1)
#define fr(i,s,t) for (i=s;i<=t;i++)
using namespace std;
vector vec[MAXN];
int W,N,M,K,L[MAXN],H[MAXN],temp[MAXN];
LL Xx[MAXN],Yy[MAXN],Temp[MAXN],C[MAXN+20][13],ans;
bool f[MAXN];
struct Point{
int x,y;
}P[MAXN];
struct Node{
int l,r;
LL val;
}T[MAXN*10];
LL Read(){
LL x=0; char ch=0;
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x;
}
void InIt(){
LL N1,M1;
cin>>N1>>M1>>W;
N1++; M1++;
int i,j;
for (i=1;i<=W;i++)
Xx[i]=Read(),Yy[i]=Read(),Xx[i]++,Yy[i]++;
fr(i,1,W) Temp[i]=Xx[i];
sort(Temp+1,Temp+W+1);
N=unique(Temp+1,Temp+W+1)-Temp-1;
fr(i,1,W) P[i].x=lower_bound(Temp+1,Temp+N+1,Xx[i])-Temp;
fr(i,1,W) Temp[i]=Yy[i];
sort(Temp+1,Temp+W+1);
M=unique(Temp+1,Temp+W+1)-Temp-1;
fr(i,1,W) P[i].y=lower_bound(Temp+1,Temp+M+1,Yy[i])-Temp;
scanf("%d",&K);
for (i=1;i<=W;i++) {
L[P[i].x]++; H[P[i].y]++;
vec[P[i].x].push_back(P[i].y);
}
C[0][0]=1;
for (i=1;i<=W;i++){
C[i][0]=1;
for (j=1;j<=min(i,K);j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
C[i][j]%=MOD;
}
}
void build(int rt,int l,int r){
if (l==r){
T[rt].r=H[l];
return;
}
build(ls,l,mid); build(rs,mid+1,r);
}
void update(int rt){
T[rt].val=T[ls].val+T[rs].val;
}
void Add(int rt,int l,int r,int pos){
if (l==r){
T[rt].l++; T[rt].r--;
if (T[rt].l>=K&&T[rt].r>=K) {
T[rt].val=C[T[rt].l][K]*C[T[rt].r][K];
if (T[rt].val>=MOD) T[rt].val%=MOD;
}
else T[rt].val=0;
return;
}
if (pos<=mid) Add(ls,l,mid,pos);
else Add(rs,mid+1,r,pos);
update(rt);
}
int Query(int rt,int l,int r,int l1,int r1){
if (l>=l1&&r<=r1) return T[rt].val;
LL res=0;
if (l1<=mid) res+=Query(ls,l,mid,l1,r1);
if (r1>mid) res+=Query(rs,mid+1,r,l1,r1);
return res;
}
int main(){
// freopen("religious.in","r",stdin);
// freopen("religious.out","w",stdout);
InIt();
int i,k,j;
build(0,1,M);
fr(i,1,N){
k=0;
memset(temp,0,sizeof(temp));
for (j=0;jr) {
x++;//开始忘了
continue;
}
LL num=Query(0,1,M,l,r);
ans+=num*C[K+x][K]*C[L[i]-K-x][K];
if (ans>=MOD) ans%=MOD;
x++;
}
fr(j,1,k)
if (H[temp[j]]>=2*K)//贪心优化一下,如果该行一定不可以就不用加了
Add(0,1,M,temp[j]);
}
cout<