P2154 [SDOI2009]虔诚的墓主人题解
题目描述
小W是一片新造公墓的管理人。公墓可以看成一块N×M的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地。
当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地。为了体现自己对主的真诚,他们希望自己的墓地拥有着较高的虔诚度。
一块墓地的虔诚度是指以这块墓地为中心的十字架的数目。一个十字架可以看成中间是墓地,墓地的正上、正下、正左、正右都有恰好k棵常青树。
小W希望知道他所管理的这片公墓中所有墓地的虔诚度总和是多少。
输入格式
输入文件religious.in的第一行包含两个用空格分隔的正整数N和M,表示公墓的宽和长,因此这个矩形公墓共有(N+1) ×(M+1)个格点,左下角的坐标为(0, 0),右上角的坐标为(N, M)。
第二行包含一个正整数W,表示公墓中常青树的个数。
第三行起共W行,每行包含两个用空格分隔的非负整数xi和yi,表示一棵常青树的坐标。输入保证没有两棵常青树拥有相同的坐标。
最后一行包含一个正整数k,意义如题目所示。
输出格式
输出文件religious.out仅包含一个非负整数,表示这片公墓中所有墓地的虔诚度总和。为了方便起见,答案对2,147,483,648取模。
输入输出样例
5 6 13 0 2 0 3 1 2 1 3 2 0 2 1 2 4 2 5 2 6 3 2 3 3 4 3 5 2 2
输出
6
思路:
很容易看出,对于一个空地(x,y),如果上下左右分别有u,d,l,r棵常青树,且u,d,l,r>=ku,d,l,r>=k,那么这块墓地的虔诚度为C(u,k)*C(d,k)*C(l,k)*C(r,k)C(u,k)∗C(d,k)∗C(l,k)∗C(r,k)。
由于n,m过大,所以我们枚举树的个数,但w<=1e5,O(n2)过不了,所以我们要先离散化下坐标,这是很容易想到的事情,可以大量提高搜索效率。离散化后又该如何处理就成了问题,我们用x作为第一关键字,作为第二关键字排下序,
这样,就可以去枚举纵坐标,求出纵坐标的组合数,但横坐标的呢?
我们可以维护一个数组,让它去维护当前x坐标y坐标的左右两侧组合数的乘积,这样即可求出答案。这样的数组需要支持区间求和(毕竟,x相同时,y坐标的差距也可能很大,依次遍历也会超时的),同时还要支持单点修改(从左向右枚举x,遇到树求完答案后,更新左右组合数的值),
所以想到用树状数组来维护数组,注意模数即可。
代码如下:
#include<bits/stdc++.h> using namespace std; #define int long long #define x first #define y second inline char gc(){ static char buf[1<<20],*p1=buf,*p2=buf; if(p1==p2){ p2=(p1=buf)+fread(buf,1,1<<20,stdin); if(p1==p2) return EOF; } return *p1++; } inline int read(){ int x=0;char ch=gc(); while(!isdigit(ch)) ch=gc(); while(isdigit(ch)) x=x*10+ch-'0',ch=gc(); return x; } int w; typedef pair<int,int> PII; const int N=1e5+5,mod=2147483648LL; int x[N],y[N],c[N][15]; int t[N],s[N]; int NumOfX[N],NumOfY[N]; PII p[N]; inline int lowbit(int x){ return x&(-x); } inline void update(int i,int k){ k%=mod; while(i<=w){ s[i]=(s[i]+k)%mod; i+=lowbit(i); } } inline int query(int i){ int ret=0; while(i){ ret=(ret+s[i])%mod; i-=lowbit(i); } return ret; } signed main(){ read();read(); w=read(); for(int i=1;i<=w;++i) x[i]=p[i].x=read(),y[i]=p[i].y=read(); int k=read(); c[0][0]=1; for(int i=1;i<=w;++i){ c[i][0]=1; for(int j=1;j<=k;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; } sort(x+1,x+w+1); sort(y+1,y+w+1); for(int i=1;i<=w;++i) p[i].x=lower_bound(x+1,x+w+1,p[i].x)-x,NumOfX[p[i].x]++; for(int i=1;i<=w;++i) p[i].y=lower_bound(y+1,y+w+1,p[i].y)-y,NumOfY[p[i].y]++; sort(p+1,p+w+1); int DOWN=1,ans=0; for(int i=1;i<w;++i){ if(p[i].x==p[i-1].x) DOWN++; else DOWN=1; int UP=NumOfX[p[i].x]-DOWN; if(UP){ int Ceil=p[i+1].y-1; ans+=c[DOWN][k]*c[UP][k]%mod*(((query(Ceil)-query(p[i].y)%mod+mod)%mod))%mod; } ++t[p[i].y]; int now=c[t[p[i].y]][k]*c[NumOfY[p[i].y]-t[p[i].y]][k]%mod; int pre=((query(p[i].y)-query(p[i].y-1))%mod+mod)%mod; update(p[i].y,((now-pre)%mod+mod)%mod); } printf("%lld\n",(ans%mod+mod)%mod); return 0; }
浙公网安备 33010602011771号