「解题报告」P2154 虔诚的墓主人

「解题报告」P2154 虔诚的墓主人

原题传送门

题意

\(n\times m\) 一个方格上给你 \(w\) 个点,求方格里每个点正上下左右各选 \(k\) 个点的方案数。

\(1 \le N, M \le 1,000,000,000,0 \le x_i \le N,0 \le y_i \le M,1 \le W \le 100,000,1 \le k \le 10\)

思路

首先看到 \(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

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

*/
posted @ 2022-04-09 20:33  K8He  阅读(97)  评论(2编辑  收藏  举报