BZOJ1227 [SDOI2009]虔诚的墓主人 【树状数组】

题目

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

输入格式

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

输出格式

包含一个非负整数,表示这片公墓中所有墓地的虔诚度总和。为了方便起见,答案对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

提示

图中,以墓地(2, 2)和(2, 3)为中心的十字架各有3个,即它们的虔诚度均为3。其他墓地的虔诚度为0。

所有数据满足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。

注意:”恰好有k颗树“,这里的恰好不是有且只有,而是从>=k的树中恰好选k棵

题解

题目中的模数等于\(2^31\),所以int自然溢出就相当于取模

我们记一个点上下左右的树数量为u、d、l、r,则每个点的贡献是\(C_{u}^{k} * C_{d}^{k} * C_{l}^{k} * C_{r}^{k}\)

点的范围很大,我们将其离散化到100000以内

但总共\(W^2\)个点,不能直接算,但树只有\(W\)个,考虑从树出发

我们将所有树排序后,对于横坐标相同的两棵树之间的点,其式子中的\(C_{u}^{k} * C_{d}^{k}\)是一样的

我们用树状数组维护\(C_{l}^{k} * C_{r}^{k}\),就可以加速运算了

#include<iostream>
#include<cstdio>
#include<algorithm>
#define lbt(x) (x & -x)
using namespace std;
const int maxn = 100005,maxm = 100005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57) {if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - ‘0‘; c = getchar();}
    return out * flag;
}
int s[maxn],C[maxn][11],b[maxn],tot,n,k;
int U[maxn],D[maxn],L[maxn],R[maxn],V[maxn];
struct point{int x,y;}p[maxn];
int getn(int x){return lower_bound(b + 1,b + 1 + tot,x) - b;}
inline bool operator <(const point& a,const point& b){
    return a.x == b.x ? a.y < b.y : a.x < b.x;
}
void add(int u,int v){while (u <= tot) s[u] += v,u += lbt(u);}
void mus(int u,int v){while (u <= tot) s[u] -= v,u += lbt(u);}
int query(int u){int ans = 0; while (u) ans += s[u],u -= lbt(u); return ans;}
int sum(int l,int r){return query(r) - query(l - 1);}
void cal(){
    for (int i = 0; i <= n; i++){
        C[i][0] = C[i][i] = 1;
        for (int j = 1; j <= i && j <= 10; j++)
            C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
    }
}
void init(){
    read(); read();
    n = read();
    cal();
    for (int i = 1; i <= n; i++)
        b[i] = p[i].x = read(),p[i].y = read();
    sort(b + 1,b + 1 + n); tot = 1;
    for (int i = 2; i <= n; i++) if (b[i] != b[tot]) b[++tot] = b[i];
    for (int i = 1; i <= n; i++) p[i].x = getn(p[i].x);
    for (int i = 1; i <= n; i++) b[i] = p[i].y;
    sort(b + 1,b + 1 + n); tot = 1;
    for (int i = 2; i <= n; i++) if (b[i] != b[tot]) b[++tot] = b[i];
    for (int i = 1; i <= n; i++) p[i].y = getn(p[i].y);
    k = read();
}
void solve(){
    sort(p + 1,p + 1 + n);
    int ans = 0;
    for (int i = 1; i <= n; i++) U[p[i].x]++,R[p[i].y]++;
    for (int i = 1; i <= n; i++){
        if (i > 1 && p[i - 1].x == p[i].x && p[i - 1].y + 1 < p[i].y)
            ans += C[U[p[i].x]][k] * C[D[p[i].x]][k] * sum(p[i - 1].y + 1,p[i].y - 1);
        U[p[i].x]--; D[p[i].x]++;
        R[p[i].y]--; L[p[i].y]++;
        add(p[i].y,-V[p[i].y]);
        add(p[i].y,V[p[i].y] = C[L[p[i].y]][k] * C[R[p[i].y]][k]);
    }
    cout << (ans >= 0 ? ans : ans + 2147483647 + 1) << endl;
}
int main(){
    init();
    solve();
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8478222.html

时间: 2024-10-11 14:34:43

BZOJ1227 [SDOI2009]虔诚的墓主人 【树状数组】的相关文章

Bzoj 1227: [SDOI2009]虔诚的墓主人 树状数组,离散化,组合数学

1227: [SDOI2009]虔诚的墓主人 Time Limit: 5 Sec  Memory Limit: 259 MBSubmit: 895  Solved: 422[Submit][Status][Discuss] Description 小W 是一片新造公墓的管理人.公墓可以看成一块N×M 的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地.当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地.为了体现自己对主的真诚,他们希望自己的墓地拥有着较高的虔诚度

[luogu2154 SDOI2009] 虔诚的墓主人(树状数组+组合数)

传送门 Solution 显然每个点的权值可以由当前点上下左右的树的数量用组合数\(O(1)\)求出,但这样枚举会T 那么我们考虑一段连续区间,对于一行中两个常青树中间的部分左右树的数量一定,我们可用树状数组求区上下贡献值和,相乘就得到了当前区间的贡献. 有思路调不出来系列 Code #include <cmath> #include <cstdio> #include <cstring> #include <cstdlib> #include <io

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

1227: [SDOI2009]虔诚的墓主人 Time Limit: 5 Sec  Memory Limit: 259 MBSubmit: 803  Solved: 372[Submit][Status][Discuss] Description 小W 是一片新造公墓的管理人.公墓可以看成一块N×M 的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地.当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地.为了体现自己对主的真诚,他们希望自己的墓地拥有着较高的虔诚度

[bzoj1227] [SDOI2009]虔诚的墓主人

终于填上了这个万年巨坑....从初二的时候就听说过这题...然后一直不敢写QAQ 现在感觉也不是很烦(然而我还是写麻烦了 离散化一波,预处理出组合数什么的.. 要维护对于当前行,每列上方和下方节点凑出合法方案的个数. 然后对于当前行上两棵相邻的常青树,求一下左边.右边合法方案数,乘上中间空的列的合法方案总数就好了. 单点修改,区间查询..我竟然跑去写线段树...懒得改了.. 1 #include<cstdio> 2 #include<iostream> 3 #include<

BZOJ 1227 [SDOI2009] 虔诚的墓主人 离线+树状数组+离散化

鸣谢:140142耐心讲解缕清了我的思路 题意:由于调这道题调的头昏脑涨,所以题意自己搜吧,懒得说. 方法:离线+树状数组+离散化 解析:首先深表本蒟蒻对出题人的敬(bi)意(shi).这道题简直丧心病狂,看完题后大脑一片空白,整个人都不好了,刚开始的思路是什么呢?暴力思想枚举每个墓碑,然后计算每个墓碑的虔诚度,然后再来统计.不过看看数据范围呢?10^9*10^9的矩阵,最多才10^5个树,光枚举就已经超时了,所以肯定不行.(不过要是考试真没思路我就那么搞了- -!) 然后也想到来枚举墓碑,不过

[bzoj1878] [SDOI2009]HH的项链(树状数组+离线)

1878: [SDOI2009]HH的项链 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 3210  Solved: 1619[Submit][Status][Discuss] Description HH有一串由各种漂亮的贝壳组成的项链.HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH不断地收集新的贝壳,因此, 他的项链变得越来越长.有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同 的

「树状数组」[SDOI2009]HH的项链

[SDOI2009]HH的项链 原题链接 [SDOI2009]HH的项链 题目大意 给你 \(n\) 个数,再给你 \(q\) 次询问,每次询问给你 \(l, r\) ,问你 \(l, r\) 中有多少个不同的数 题目题解 分析这道题我们发现,对于一个 \([L_1, R_1]\) 存在另一个 \([L_2, R_1]\) 且 \(L_2\) 严格大于 \(L_1\),那么就一定存在第一个区间不同的数 大于等于 第二个区间的不同的数,这里很显然有一种等于的情况,什么情况等于?在\([L_2,R_

BZOJ1878 SDOI2009 HH的项链 树状数组

题意:给定一个颜色序列,每组询问给出区间[l,r],求[l,r]中不同颜色的数量 题解: 首先把所有颜色离散化,然后离线,将询问按右区间升序排列.从1-N把整个序列扫一遍,设Pos[i]为第i个颜色最后出现的位置,假定当前扫到的位置为i,则更新Pos[a[i]],那么问题变成了:求一个序列(Pos)中,大于等于一个数(L)的数的数量. 用树状数组维护Pos=j的数的数量,每次查询树状数组中L-N的和即可. 貌似SDOI不喜欢考大型数据结构啊……坐等今年打脸 #include <cstdio>

BZOJ 1878 SDOI2009 HH的项链 树状数组/莫队算法

题目大意:给定一个序列.求一个区间内有多少个不同的数 正解是树状数组 将全部区间依照左端点排序 然后每次仅仅统计左端点開始的每种颜色的第一个数即可了 用树状数组维护 我写的是莫队算法 莫队明显能搞 m√m明显慢了点可是还是能接受的一个复杂度 一開始离散化数组开小了各种秒RE-- 跪了 #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algori