P2154 虔诚的墓主人 题解
K8He
·
·
题解
P2154 虔诚的墓主人 题解
原题传送门
题意
在 n\times m 一个方格上给你 w 个点,求方格里每个点正上下左右各选 k 个点的方案数。
## 思路
首先看到 $N,M$ 这么大,肯定要先离散化一下。
然后考虑怎么求方案数。
我们先对离散化后的点排个序,然后考虑两个 $x$ 相同的点 $x,y1$ 和 $x,y2$ 之间的所有点的方案数。
显然是:
$$
C_{y1\_UP}^{k}\times C_{y2\_DOWN}^{k}\times \sum_{y1<l<y2}C_{l\_LEFT}^{k}\times C_{l\_RIGHT}^{k}
$$
~~你们意会一下。~~
观察这个式子,$C_{y1\_UP}^{k}\times C_{y2\_DOWN}^{k}$ 当前已知,可以用前缀和维护 $\sum C_{l\_LEFT}^{k}\times C_{l\_RIGHT}^{k}$。
那么我们就开一个树状数组,维护前 $i$ 行的 $C_{l\_LEFT}^{k}\times C_{l\_RIGHT}^{k}$ 之和,每次碰到一个点 $x,yy$ 时把当前行的影响清除,再令 $yy\_LEFT+1,yy\_RIGHT-1$,再重新计入前缀和。
可以参考代码中 Solve 函数中变量 $u$ 的求法。
时间复杂度 $O(nlogn)$。
~~我这个菜鸡居然因为取模取错了调了两节课。~~
## Code
```cpp
#include <bits/stdc++.h>
#define _for(i,a,b) for(ll i=a;i<=b;++i)
#define for_(i,a,b) for(ll i=a;i>=b;--i)
#define ll long long
using namespace std;
const ll N=1e5+10,P=2147483648;
ll n,m,w,k,q[N],h[N],z[N],y[N],ans;
struct tree{ll x,y;}t[N];
inline ll rnt(){
ll x=0,w=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*w;
}
namespace SZSZ{
/*树状数组*/
ll b[N];
inline ll lowbit(ll x){return x&-x;}
inline void UpDate(ll x,ll y){
while(x<=w){
b[x]=(b[x]+y)%P;
x+=lowbit(x);
}
return;
}
inline ll Query(ll x){
if(x==0)return 0;
ll sum=0;
while(x){
sum=(sum+b[x])%P;
x-=lowbit(x);
}
return sum;
}
}
namespace LISAN{
/*离散化*/
vector<ll>xx,yy;
inline bool cmp(tree a,tree b){
if(a.x==b.x)return a.y<b.y;
return a.x<b.x;
}
inline void Add(ll x,ll y){
xx.push_back(x);
yy.push_back(y);
return;
}
inline void LiSan(){
sort(xx.begin(),xx.end());
sort(yy.begin(),yy.end());
xx.erase(unique(xx.begin(),xx.end()),xx.end());
yy.erase(unique(yy.begin(),yy.end()),yy.end());
_for(i,1,w){
t[i].x=lower_bound(xx.begin(),xx.end(),t[i].x)-xx.begin()+1;
t[i].y=lower_bound(yy.begin(),yy.end(),t[i].y)-yy.begin()+1;
++h[t[i].x],++y[t[i].y];
}
sort(t+1,t+w+1,cmp);
return;
}
}
namespace SOLVE{
ll c[N*20][20]={0};
/*预处理组合数*/
inline void PreC(){
c[0][0]=1;
_for(i,1,w){
c[i][0]=1;
_for(j,1,min(k,i))
c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
}
}
/*求解*/
inline ll Solve(){
PreC();
_for(i,1,w-1){
++q[t[i].x];
++z[t[i].y];
if(t[i].x==t[i+1].x&&q[t[i].x]>=k&&h[t[i].x]-q[t[i].x]>=k){
ll up=c[q[t[i].x]][k];
ll dn=c[h[t[i].x]-q[t[i].x]][k];
ll ri=SZSZ::Query(t[i+1].y-1)-SZSZ::Query(t[i].y);
ans+=((up*dn+P)%P*ri+P)%P;
ans%=P;
}
ll u=((c[z[t[i].y]][k]*c[y[t[i].y]-z[t[i].y]][k]+P)%P-(SZSZ::Query(t[i].y)-SZSZ::Query(t[i].y-1)+P)%P+P)%P;
SZSZ::UpDate(t[i].y,u);
}
return ans;
}
}
int main(){
n=rnt(),m=rnt(),w=rnt();
_for(i,1,w){
t[i].x=rnt(),t[i].y=rnt();
LISAN::Add(t[i].x,t[i].y);
}
k=rnt();
LISAN::LiSan();
printf("%lld\n",SOLVE::Solve());
return 0;
}
/*
*/
```