poj3109坐标离散化+树状数组
2015-03-06 21:09
309 查看
黑白棋・改:无限大的棋盘上,在横向和纵向上被包围的白子会变成黑子,求最终黑子个数?
扫描线算法:扫描线算法的目标是计算黄颜色多边形内部的像素点,最终将其涂色。
基本思想
按扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的象素,即完成填充工作。
对于一条扫描线填充过程可以分为四个步骤:
(1) 求交:计算扫描线与多边形各边的交点
(2) 排序:把所有交点按 x 坐标递增顺序来排序
(3) 配对:确定扫描线与多边形的相交区间,第一个与第二个,第三个与第四个等等,每对交点代表扫描线与多边形的一个相交区间
(4) 填充:显示相交区间的象素
统计x的时候,需要将一个区间[交点n的x + 1,交点n+1的x - 1]的个数统统加一,树状数组本身做加法的话是logn,做n次的话是nlogn,太浪费了,BIT可以高效地求一段区间和,以及更新单个元素的值,但是无法高效地更新一个区间的值。
于是可以用双BIT来实现,一个维护初始个数,一个维护累加值,可以将复杂度控制在logn。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define MAX_N 100000+16
typedef long long LL;
LL bit0[MAX_N],bit1[MAX_N];//双Bit实现O(lgn)
static int N,X[MAX_N],Y[MAX_N];
static vector<int>line[MAX_N];//每个黑子构成的一条条水平线
static bool visited[MAX_N];//是否已经算过了
//求和sum(a[t],0<=t<=i
LL sum(LL*b,int i){
int s = 0;
while(i>0){
s+=b[i];
i -=(i&-i);
}
return s;
}
//求和从a[from,to)
LL sum(LL*b,int from,int to){
return sum(b,to-1) - sum(b,from-1);
}
//执行a[i]+=v
void add(LL*b,int i,LL v){
while(i<=MAX_N)
{
b[i] +=v;
i+=(i&-i);
}
}
//基础Bit 结束
//双BIT
//维护的是区间的增量
//[0,n]
//只要在l,r+1处赋值那么就可以求得从[l,r]的增量和
LL sum(int i){
return sum(bit1,i)*i + sum(bit0,i);
}
//(from,to]
LL sum(int from,int to){
return sum(to) - sum(from);
}
//[from,to]
void add(int from,int to,LL x){
add(bit0,from,-x*(from-1));
add(bit1,from,x);
add(bit0,to,x*to);
add(bit1,to,-x);
}
//压缩的方法就是有几种坐标
//x为坐标数组,长度为N
int compress(int *p){
vector<int>ps(N);
for (int i=0;i<N;++i)
{
ps[i] = p[i];
}
sort(ps.begin(),ps.end());
ps.erase(unique(ps.begin(),ps.end()),ps.end());
for (int i=0;i<N;++i)
{
p[i] = 1+distance(ps.begin(),lower_bound(ps.begin(),ps.end(),p[i]));
//算出p[i]在第几个位置而确定是第几种x坐标
}
return ps.size();
}
int main(int argc,char**argv){
scanf("%d",&N);
for(int i=0;i<N;++i)
scanf("%d%d",X+i,Y+i);
int w = compress(X);//压缩X坐标
int h = compress(Y);
//压缩Y坐标后程扫描线
//就是第几种Y放第几种X
for (int i=0;i<N;++i)
{
line[Y[i]].push_back(X[i]);//一条条扫面线
}
LL result = N;
for(int y=1;y<=h;++y){
//对每条扫描线计算上面空白点的个数
vector<int>&xs = line[y];//纵坐标为ydd的那些点横坐标
sort(xs.begin(),xs.end());
for (vector<int>::iterator i = xs.begin();i!=xs.end();++i)
{
int x = *i;
LL s = sum(x-1,x);//横坐标为x的空白点个数(x-1,x]区间的增数
if (visited[x])
result += s;//要染色的像素点
else
visited[x] = true;
add(bit0,x,-s);//add(x,x,-s)复位操作
if (i+1!=xs.end())
{
//到下一个黑点之间的空白点个数
if (x+1<*(i+1)-1)//下一个黑子这样多于1个空白的时候更新的是区间
add(x+1,*(i+1)-1,1);//遇到了空白
else if (x+1==*(i+1)-1)//否则相当于更新单个点
add(bit0,x+1,1);//此时等同于add(x + 1, x + 1, 1);
}
}
}
cout<<result<<endl;
return 0;
}
扫描线算法:扫描线算法的目标是计算黄颜色多边形内部的像素点,最终将其涂色。
基本思想
按扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的象素,即完成填充工作。
对于一条扫描线填充过程可以分为四个步骤:
(1) 求交:计算扫描线与多边形各边的交点
(2) 排序:把所有交点按 x 坐标递增顺序来排序
(3) 配对:确定扫描线与多边形的相交区间,第一个与第二个,第三个与第四个等等,每对交点代表扫描线与多边形的一个相交区间
(4) 填充:显示相交区间的象素
统计x的时候,需要将一个区间[交点n的x + 1,交点n+1的x - 1]的个数统统加一,树状数组本身做加法的话是logn,做n次的话是nlogn,太浪费了,BIT可以高效地求一段区间和,以及更新单个元素的值,但是无法高效地更新一个区间的值。
于是可以用双BIT来实现,一个维护初始个数,一个维护累加值,可以将复杂度控制在logn。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define MAX_N 100000+16
typedef long long LL;
LL bit0[MAX_N],bit1[MAX_N];//双Bit实现O(lgn)
static int N,X[MAX_N],Y[MAX_N];
static vector<int>line[MAX_N];//每个黑子构成的一条条水平线
static bool visited[MAX_N];//是否已经算过了
//求和sum(a[t],0<=t<=i
LL sum(LL*b,int i){
int s = 0;
while(i>0){
s+=b[i];
i -=(i&-i);
}
return s;
}
//求和从a[from,to)
LL sum(LL*b,int from,int to){
return sum(b,to-1) - sum(b,from-1);
}
//执行a[i]+=v
void add(LL*b,int i,LL v){
while(i<=MAX_N)
{
b[i] +=v;
i+=(i&-i);
}
}
//基础Bit 结束
//双BIT
//维护的是区间的增量
//[0,n]
//只要在l,r+1处赋值那么就可以求得从[l,r]的增量和
LL sum(int i){
return sum(bit1,i)*i + sum(bit0,i);
}
//(from,to]
LL sum(int from,int to){
return sum(to) - sum(from);
}
//[from,to]
void add(int from,int to,LL x){
add(bit0,from,-x*(from-1));
add(bit1,from,x);
add(bit0,to,x*to);
add(bit1,to,-x);
}
//压缩的方法就是有几种坐标
//x为坐标数组,长度为N
int compress(int *p){
vector<int>ps(N);
for (int i=0;i<N;++i)
{
ps[i] = p[i];
}
sort(ps.begin(),ps.end());
ps.erase(unique(ps.begin(),ps.end()),ps.end());
for (int i=0;i<N;++i)
{
p[i] = 1+distance(ps.begin(),lower_bound(ps.begin(),ps.end(),p[i]));
//算出p[i]在第几个位置而确定是第几种x坐标
}
return ps.size();
}
int main(int argc,char**argv){
scanf("%d",&N);
for(int i=0;i<N;++i)
scanf("%d%d",X+i,Y+i);
int w = compress(X);//压缩X坐标
int h = compress(Y);
//压缩Y坐标后程扫描线
//就是第几种Y放第几种X
for (int i=0;i<N;++i)
{
line[Y[i]].push_back(X[i]);//一条条扫面线
}
LL result = N;
for(int y=1;y<=h;++y){
//对每条扫描线计算上面空白点的个数
vector<int>&xs = line[y];//纵坐标为ydd的那些点横坐标
sort(xs.begin(),xs.end());
for (vector<int>::iterator i = xs.begin();i!=xs.end();++i)
{
int x = *i;
LL s = sum(x-1,x);//横坐标为x的空白点个数(x-1,x]区间的增数
if (visited[x])
result += s;//要染色的像素点
else
visited[x] = true;
add(bit0,x,-s);//add(x,x,-s)复位操作
if (i+1!=xs.end())
{
//到下一个黑点之间的空白点个数
if (x+1<*(i+1)-1)//下一个黑子这样多于1个空白的时候更新的是区间
add(x+1,*(i+1)-1,1);//遇到了空白
else if (x+1==*(i+1)-1)//否则相当于更新单个点
add(bit0,x+1,1);//此时等同于add(x + 1, x + 1, 1);
}
}
}
cout<<result<<endl;
return 0;
}
相关文章推荐
- hdu 5862 Counting Intersections 坐标离散化+树状数组
- 51NOD 1107 斜率小于0的连线数量 坐标离散化+树状数组
- HDU4456————Crowd(离散化,坐标转化,二维树状数组)
- POJ3109_Inner Vertices_扫描线段|坐标离散化|树状数组
- poj 2528 坐标离散化+线段树
- poj 2299 Ultra-QuickSort(归并排序)||(树状数组+离散化)
- 大二训练第二周 D - 覆盖的面积 离散化坐标加线段树
- HDU 5876 Disharmony Trees(离散化 树状数组)
- poj2299 (树状数组+离散化)
- hihoCoder#1079(线段树+坐标离散化)
- HDU 5101 Select --离散化+树状数组
- Aizu - 0531 Paint Color (坐标离散化)
- 《挑战程序设计竞赛》 坐标离散化技巧实现
- hdu 3874 树状数组+离散化
- 树状数组+离散化——HDU 5877 (2016 ACM/ICPC Asia Regional Dalian Online 1010)
- HDOJ5877(dfs序+离散化+树状数组)
- sdut2610---Boring Counting(离线+树状数组+离散化)
- NOIP模拟题 2016.11.5 [贪心] [坐标离散化] [循环序列LIS]
- 坐标离散化技巧
- poj 2299 Ultra-QuickSort(归并排序)||(树状数组+离散化)