P2154 虔诚的墓主人 题解

· · 题解

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; } /* */ ```