山东济南彤昌机械科技有限公司 山东济南江鹏工贸游有限公司

bzoj1227 [SDOI2009]虔诚的墓主人(组合公式+离散化+线段树)

1227: [SDOI2009]虔诚的墓主人

Time Limit: 5 Sec  Memory Limit: 259 MB
Submit: 803  Solved: 372
[Submit][Status][Discuss]

Description

小W 是一片新造公墓的管理人。公墓可以看成一块N×M 的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地。当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地。为了体现自己对主的真诚,他们希望自己的墓地拥有着较高的虔诚度。一块墓地的虔诚度是指以这块墓地为中心的十字架的数目。一个十字架可以看成中间是墓地,墓地的正上、正下、正左、正右都有恰好k 棵常青树。小W 希望知道他所管理的这片公墓中所有墓地的虔诚度总和是多少

Input

第一行包含两个用空格分隔的正整数N 和M,表示公墓的宽和长,因此这个矩形公墓共有(N+1) ×(M+1)个格点,左下角的坐标为(0, 0),右上角的坐标为(N, M)。第二行包含一个正整数W,表示公墓中常青树的个数。第三行起共W 行,每行包含两个用空格分隔的非负整数xi和yi,表示一棵常青树的坐标。输入保证没有两棵常青树拥有相同的坐标。最后一行包含一个正整数k,意义如题目所示。

Output

包含一个非负整数,表示这片公墓中所有墓地的虔诚度总和。为了方便起见,答案对2,147,483,648 取模。

Sample Input

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

Sample Output

6

HINT

 

图中,以墓地(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。

 

 

【思路】

        组合公式+离散化+区间查询&单点修改。

   如果设每一个空格左右上下各有的树之数目为left[][],right[][],up[][],down[][],则答案为

    

   考虑到nm很大,这里我们先进行离散化,离散化之后时间复杂度为O(ww)。

        考虑y同的两棵相邻的树(中间为空格)且x分别为ab,我们统计夹在中间的空格的分数,则为

      

        前两项相同,因此只需要统计ab区间内的C之积即可,可以用线段树在O(logn)的时间内完成。

        具体操作:

    依次扫描每一棵树:

           如果与前一棵树同y则累计答案。

           维护cy表示当前y扫描过的树之数目。

              维护cx[]表示x被扫描过的树之数目。

            维护线段树。 

         需要注意的是n在线段树中定义为离散后x的最大下标。

 

【代码】

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
  6 using namespace std;
  7 
  8 typedef long long LL;
  9 const int maxn = 400000+10;
 10 const LL MOD = 2147483648LL;
 11 struct Node{
 12     int x,y;
 13     bool operator<(const Node& rhs) const{
 14         return y<rhs.y || (y==rhs.y && x<rhs.x);
 15     }
 16 }nodes[maxn];
 17 
 18 int read() {
 19     char c=getchar();
 20     while(!isdigit(c)) c=getchar();
 21     int x=0;
 22     while(isdigit(c)) {
 23         x=x*10+c-'0';
 24         c=getchar();
 25     }
 26     return x;
 27 }
 28 //线段树相关
 29 //+单点修改+区间求和
 30 LL sumv[4*maxn];
 31 int v; LL d;
 32 void update(int u,int L,int R) {
 33     int lc=u*2,rc=lc+1;
 34     if(L==R) {
 35         sumv[u]=d;
 36     }
 37     else {
 38         int M=L+(R-L)/2;
 39         if(v<=M) update(lc,L,M);
 40         else update(rc,M+1,R);
 41         sumv[u]=sumv[lc]+sumv[rc];
 42     }
 43 }
 44 int y1,y2;
 45 LL query(int u,int L,int R) {
 46     int lc=u*2,rc=lc+1;
 47     if(y1<=L && R<=y2) {
 48         return sumv[u];
 49     }
 50     else {
 51         int M=L+(R-L)/2;
 52         LL res=0;
 53         if(y1<=M) res += query(lc,L,M);
 54         if(M<y2)  res += query(rc,M+1,R);
 55         res %= MOD;
 56         return res;
 57     }
 58 }
 59 //组合函数相关 
 60 LL C[maxn][20];
 61 void get_C(int n) {
 62     C[0][0]=1;
 63     for(int i=1;i<=n;i++) {
 64         C[i][0]=C[i][i]=1;
 65         for(int j=1;j<=10;j++)
 66             C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
 67     }
 68 }
 69 
 70 int cx[maxn],sumx[maxn],sumy[maxn];
 71 int hash[5*maxn],x[maxn],y[maxn];
 72 int n,m,w,k;
 73 
 74 int main() {
 75     n=read(),m=read(),w=read();
 76     get_C(w);
 77     int p=1;
 78     FOR(i,1,w) {
 79         x[i]=read(),y[i]=read();
 80         hash[p++]=x[i],hash[p++]=y[i];
 81     }
 82     p--;
 83     k=read();
 84     //离散化坐标 
 85     sort(hash+1,hash+p+1);
 86     p=unique(hash+1,hash+p+1)-hash; p--;
 87     n=0;
 88     FOR(i,1,w) {
 89         x[i]=lower_bound(hash+1,hash+p+1,x[i])-hash;
 90         y[i]=lower_bound(hash+1,hash+p+1,y[i])-hash;
 91         n=max(n,x[i]);                                     //n用于线段树表示最大x下标 
 92         sumx[x[i]]++,sumy[y[i]]++;
 93         nodes[i]=(Node){x[i],y[i]};
 94     }
 95     sort(nodes+1,nodes+w+1);
 96     //扫描每一棵树 
 97     LL ans=0,cy=0;
 98     FOR(i,1,w) {
 99         int r=nodes[i].x,c=nodes[i].y;
100         if(i>1 && c==nodes[i-1].y) {
101             y1=nodes[i-1].x+1,y2=nodes[i].x-1;
102             if(y1<=y2) ans = (ans+C[cy][k]*C[sumy[c]-cy][k]%MOD*query(1,1,n)%MOD)%MOD;
103             cy++;
104         }
105         else cy=1;
106         
107         cx[r]++;
108         v=r; d=C[cx[r]][k]*C[sumx[r]-cx[r]][k]%MOD;
109         update(1,1,n);
110     }
111     printf("%lld\n",ans);
112     return 0;
113 }

 

posted on 2015-11-20 21:42  hahalidaxin  阅读(246)  评论(0编辑  收藏  举报