[SDOI2009]虔诚的墓主人

来源:互联网 发布:苹果电脑视频编辑软件 编辑:程序博客网 时间:2024/05/01 05:01
[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取模。
输入输出样例
输入样例#1:
5 6130 20 31 21 32 02 12 42 52 63 23 34 35 22
输出样例#1:
6
说明
图中,以墓地(2, 2)和(2, 3)为中心的十字架各有3个,即它们的虔诚度均为3。其他墓
地的虔诚度为0。
对于30%的数据,满足1 ≤ N, M ≤ 1,000。
对于60%的数据,满足1 ≤ N, M ≤ 1,000,000。
对于100%的数据,满足1 ≤ N, M ≤ 1,000,000,000,0 ≤ xi ≤ N,0 ≤ yi ≤ M,1 ≤ W ≤ 100,000,1 ≤ k ≤ 10。
存在50%的数据,满足1 ≤ k ≤ 2。
存在25%的数据,满足1 ≤ W ≤ 10000。

题解:
经过简单的观察就可以发现每个点的虔诚度为:c[l[i]][k]*c[r[i]][k]*c[u[i][k]*c[d[i]][k]其中l,r,u,d分别表示上下左右有多少棵树。
但是这样做事n^2的而n事1e9,发现w范围比较小,可以从w入手。
考虑枚举树,l,r从枚举的时候就可以解决,关键就是如何求u,d。
可以先将点离散,枚举点时求相邻两棵树中间的点对于答案的贡献,换行时将这一行的点从d加到u就可以了。
发现每次修改都是单点修改区间查询,可以用树状数组维护(当然线段树也是可以的),但是不能直接维护树的个数,可以直接维护每个点的c[u[i]][k],c[d[i]][k]。
最后由于w和k比较小,直接预处理组合数就可以了。

代码:
#include<bits/stdc++.h>using namespace std;const int max_n = 100010;const int max_k = 20;const int inf = 1e9+7;struct point{int x,y,pos;}a[max_n];int tree[2][max_n],tree_c[max_n];int c[max_n][max_k],y[max_n],d1[max_n],d2[max_n];int l[max_n],r[max_n];int n,m,w,k,tot,ans;inline bool cmp2(point a,point b) {return a.y<b.y ? 1 : 0;}inline bool cmp(point a,point b){if(a.x==b.x) return a.y<b.y ? 1 : 0;else return a.x<b.x ? 1 : 0;}inline int lowbit(int i){return i&(-i);}inline void add(int loc,int val,int wh){int tmp1,tmp2;tmp1=c[tree[0][loc]][k]*c[tree[1][loc]][k];if(wh) tree[0][loc]+=val; tree[1][loc]-=val;tmp2=c[tree[0][loc]][k]*c[tree[1][loc]][k];for(int i=loc; i<=w; i+=lowbit(i)){tree_c[i]-=tmp1;tree_c[i]+=tmp2;}}inline int query(int loc){int ans=0;for(int i=loc; i>0; i-=lowbit(i))  ans+=tree_c[i];return ans;}inline void init(){for(int i=0; i<=w; ++i)  c[i][0]=1;for(int i=1; i<=w; ++i)  for(int j=1; j<=min(i,k); ++j)    c[i][j]=c[i-1][j]+c[i-1][j-1];for(int i=1; i<=w; ++i)  add(d1[a[i].pos],-1,0);for(int i=1; i<=w; ++i)  r[d2[a[i].pos]]++;}inline void work(){int num=0,l=1;for(int i=1; i<=w; ++i){if(i==1 || a[i-1].x!=a[i].x){for(int j=l; j<=i-1; ++j)      add(d1[a[j].pos],1,1);num=1,l=i;} int u=query(d1[a[i].pos]-1)-query(d1[a[i-1].pos]);if(i!=1 && a[i-1].x==a[i].x)  ans+=c[num-1][k]*c[r[d2[a[i].pos]]-num+1][k]*u;num++;}}int main(){scanf("%d%d%d",&n,&m,&w);for(int i=1; i<=w; ++i){scanf("%d%d",&a[i].x,&a[i].y);a[i].pos=i;}scanf("%d",&k);sort(a+1,a+w+1,cmp2);for(int i=1; i<=w; ++i)  if(a[i].y!=a[i-1].y || i==1) d1[a[i].pos]=++tot;  else d1[a[i].pos]=tot;sort(a+1,a+w+1,cmp);tot=0;    for(int i=1; i<=w; ++i)      if(i==1 || a[i].x!=a[i-1].x) d2[a[i].pos]=++tot;      else d2[a[i].pos]=tot;init();work();printf("%d\n",ans>=0 ? ans  : ans+2147483647+1);return 0;}



原创粉丝点击