poj3109树状数组+扫描线

题意:无限大的棋盘上,在横向和纵向上被包围的白子会变成黑子,求最终黑子个数?

分析:

首先这个棋盘十分的大,但已给黑点的个数为1e5,我们需要离散化,所谓的离散化就是数组下标的重新定义。

这里给出离散化函数,返回的是离散化后数组的个数

 1 int compress(int *p,int N)
 2 {
 3     vector<int> ps(N);
 4     for (int i = 0; i < N; ++i)ps[i] = p[i];
 5     sort(ps.begin(), ps.end());
 6     ps.erase(unique(ps.begin(), ps.end()), ps.end());
 7     for (int i = 0; i < N; ++i)
 8     {
 9         p[i] = 1 + distance(ps.begin(), lower_bound(ps.begin(), ps.end(), p[i]));
10     }
11     return ps.size();
12 }

扫描线算法:

扫描线算法其实是计算机几何里面的算法,这里引用

基本思想

按扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的象素,即完成填充工作。

对于一条扫描线填充过程可以分为四个步骤:

(1)  求交:计算扫描线与多边形各边的交点

(2)  排序:把所有交点按 坐标递增顺序来排序

(3)  配对:确定扫描线与多边形的相交区间,第一个与第二个,第三个与第四个等等,每对交点代表扫描线与多边形的一个相交区间

(4)  填充:显示相交区间的象素

那么对于这道题目来说我们如何使用?

首先构造扫描线,这里使用Y轴,X轴其实一样的

对于每一条扫描线上的点按x坐标排序,然后得到n个区间,我们需要统计在这个区间里面有多少白色的点可以变为黑点

条件是上下都被黑点包围(左右已经符合)

如何判断上下有没有黑点呢?这里巧妙的使用了一个bool数组used,used[i]表示x=i这条竖线上是否已经出现过黑点

由于扫描线算法中扫描线是按y从小到大枚举的,所以如果之前有出现过黑点,加上当前扫描的点也是黑点,那么这个区间一定是被黑点包围的。

现在来处理区间统计问题,这是树状数组的强项。(区间加减+区间求和)

实际上这里只用到了区间加减+单点查询,都差不多

现在给出算法:

首先离散化,然后构造扫描线。

接着按y从小到大枚举扫描线,对于每条扫描线的点按x从小到大排序,遍历所有的点可以得到n个区间[a[i-1],a[i]]

对于区间[a[i-1],a[i]],当前遍历到a[i],如果竖轴x=a[i]上used[x]=true 那么需要将答案加上当前竖轴上区间的白点个数query(x,x),否认设置used[x]=true(因为当前遍历的就是黑点),计算完该区间以后一定要记得将该区间的树状数组清掉,因为树状数组维护的区间和,如果不清会导致重复计算(两条扫描线之间的点),

至于更新的话很简单,就是把区间(a[i-1],a[i])都加上1,因为区间中都是满足左右被黑点包围的白点。(注意不要越界)

最后上代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <set>
 5 #include <algorithm>
 6 #include <map>
 7 #include <queue>
 8 #include<cmath>
 9 #include<vector>
10 #define maxn 100000
11 #define maxm 100010
12 #define mod 1000000000000000000
13 #define INF 0x3f3f3f3f
14 using namespace std;
15 typedef long long  ll;
16 inline int lowbit(int x){
17     return x&-x;
18 }
19 struct BIT{
20     int n;
21     ll bit[maxn+10],bit2[maxn+10];
22     void init(int N){//建立bit
23         n=N;
24         memset(bit,0,sizeof(bit));
25         memset(bit2,0,sizeof(bit2));
26     }
27     void add(ll *t,int x,ll v){//单点加减
28         for(int i=x;i<=n;i+=lowbit(i))t[i]+=v;
29     }
30     ll sum(ll *t,int x){//[1,x]前缀和
31         ll ans=0;
32         for(int i=x;i>0;i-=lowbit(i))ans+=t[i];
33         return ans;
34     }
35     void update(int l,int r,ll k){//区间加减
36         add(bit,l,k);
37         add(bit,r+1,-k);
38         add(bit2,l,k*l);
39         add(bit2,r+1,-k*(r+1));
40     }
41     ll getsum(int x){
42         return sum(bit,x)*(x+1)-sum(bit2,x);
43     }
44     ll query(int l,int r){//区间求和
45         return getsum(r)-getsum(l-1);
46     }
47 }T;
48 int X[maxn],Y[maxn];
49 int compress(int *p,int N)
50 {
51     vector<int> ps(N);
52     for (int i = 0; i < N; ++i)ps[i] = p[i];
53     sort(ps.begin(), ps.end());
54     ps.erase(unique(ps.begin(), ps.end()), ps.end());
55     for (int i = 0; i < N; ++i)
56     {
57         p[i] = 1 + distance(ps.begin(), lower_bound(ps.begin(), ps.end(), p[i]));
58     }
59     return ps.size();
60 }
61 bool used[maxn];
62 vector<int>line[maxn];//扫描线
63 int main (){
64     int n;
65     while(scanf("%d",&n)!=EOF){
66         T.init(maxn);
67         memset(used,false,sizeof(used));
68         for(int i=0;i<n;++i)scanf("%d%d",&X[i],&Y[i]);
69         int w = compress(X,n);
70         int h = compress(Y,n);
71         for(int i=0;i<n;++i){
72             line[Y[i]].push_back(X[i]);
73         }
74         ll ans =n;
75         for(int i = 1;i<=h;++i){
76             vector<int>&v = line[i];
77             sort(v.begin(),v.end());
78             for(vector<int>::iterator j = v.begin();j!=v.end();++j){
79                 int x = *j;
80                 ll s = T.query(x,x);
81                 if(used[x])ans+=s;
82                 else used[x]=true;
83                 T.update(x,x,-s);
84                 if(j!=v.end()-1)T.update(x+1,*(j+1)-1,1);
85             }
86         }
87         printf("%lld\n",ans);
88     }
89 }

poj3109树状数组+扫描线

时间: 2024-09-29 10:13:36

poj3109树状数组+扫描线的相关文章

POJ 2464 Brownie Points II 树状数组+扫描线

题意奇葩的一笔,本质上就是一个复杂统计,智商低下的我想不出来只好去搜了题解 #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include <string> #include <queue> #include <deque> #inclu

BZOJ 2758 Blinker的噩梦(扫描线+熟练剖分+树状数组)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2758 题意:平面上有n个多边形(凸包和圆).任意两个多边形AB只有两种关系:(1)A包含B或者B包含A:(2)AB的公共面积为0.每个多边形有一个值x.m个查询.分两种:(1)修改某个多边形的值:(2)从一点s走到另一点t.每次走出一个多边形或者进入一个多边形时,都要抑或上该多边形的值.输出走到t时的值.(由抑或的性质和本题定义可得这个值跟走的路经无关) 思路:首先我们发现,这些

[扫描线+树状数组]解决矩形包含点的问题

今天做到第二题,大部分的思路都理解了之后最后剩下一个问题 zzx:“然后扫描线+树状数组搞一下就好了” 看到这两个算法就产生了一种我肯定会的错觉... 然后后来发现并不会的时候很惭愧... 然后十分感谢YDC,在之前完全陌生的情况下 我去问这样一个问题 超级超级超级耐心地给我解答  我问着每一个他们看起来显然的具体处理方法 突然发现真的像张老师说的:“你普及升提高的这条路没有怎么走,中间那块知识是空着的啊” 今天才切实体会到...但是或许正是这样今天学到这样的一个东西更让我觉得开心吧 树状数组解

HDU 5862 Counting Intersections (离散化+扫描线+树状数组)

题意:给你若干个平行于坐标轴的,长度大于0的线段,且任意两个线段没有公共点,不会重合覆盖.问有多少个交点. 析:题意很明确,可是并不好做,可以先把平行与x轴和y轴的分开,然后把平行y轴的按y坐标从小到大进行排序,然后我们可以枚举每一个平行x轴的线段, 我们可以把平行于x轴的线段当做扫描线,只不过有了一个范围,每次要高效的求出相交的线段数目,可以用一个优先队列来维护平行y轴的线段的上坐标, 如果在该平行于x轴的范围就给相应的横坐标加1,这样就很容易想到是用树状数组来维护,然后每次求出最左边和最右边

hdu 1556 Color the ball (扫描线+树状数组)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1556 Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 14237    Accepted Submission(s): 7120 Problem Description N个气球排成一排,从左到右依次编号为1,

【BZOJ4009】[HNOI2015]接水果 DFS序+整体二分+扫描线+树状数组

[BZOJ4009][HNOI2015]接水果 Description 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black,  她觉得这个游戏太简单了,于是发明了一个更加难的版本.首先有一个地图,是一棵由 n 个顶点.n-1 条边组成的树(例如图 1给出的树包含 8 个顶点.7 条边).这颗树上有 P 个盘子,每个盘子实际上是一条路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值.第 i 个盘子

FZU 2225 小茗的魔法阵 扫描线+树状数组

这个题和一个CF上的找"Z"的题差不多,都是扫描线+树状数组 从右上角的主对角线开始扫描,一直扫到左下角,每次更新,右延伸等于该扫描线的点,注意在其所在的树状数组更新就好了 时间复杂度O(n^2logn) #include <stdio.h> #include <iostream> #include <vector> #include <math.h> #include <set> #include <map> #

【bzoj4540】[Hnoi2016]序列 单调栈+离线+扫描线+树状数组区间修改

题目描述 给出一个序列,多次询问一个区间的所有子区间最小值之和. 输入 输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数.接下来一行,包含n个整数,以空格隔开,第i个整数为ai,即序列第i个元素的值.接下来q行,每行包含两个整数l和r,代表一次询问. 输出 对于每次询问,输出一行,代表询问的答案. 样例输入 5 5 5 2 4 1 3 1 5 1 3 2 4 3 5 2 5 样例输出 28 17 11 11 17 题解 单调栈+离线+扫描线+树状数组区间修改 首先把使用单调栈找出每个

【BZOJ1818】[Cqoi2010]内部白点 扫描线+树状数组

[BZOJ1818][Cqoi2010]内部白点 Description 无限大正方形网格里有n个黑色的顶点,所有其他顶点都是白色的(网格的顶点即坐标为整数的点,又称整点).每秒钟,所有内部白点同时变黑,直到不存在内部白点为止.你的任务是统计最后网格中的黑点个数. 内部白点的定义:一个白色的整点P(x,y)是内部白点当且仅当P在水平线的左边和右边各至少有一个黑点(即存在x1 < x < x2使得(x1,y)和(x2,y)都是黑点),且在竖直线的上边和下边各至少有一个黑点(即存在y1 <