B1818 [Cqoi2010]内部白点 树状数组

这个题的想法很好想,就是进行排序之后直接检查每个点的上下左右是否有黑点就行.但是直接枚举显然不行,那怎么办呢?我们就用树状数组维护扫描线,把每排左右点看成一条线覆盖,然后从下往上扫,遇到下加一,遇到上减一并记录答案.这样用扫描线维护就行了.

题干:

Description
无限大正方形网格里有n个黑色的顶点,所有其他顶点都是白色的(网格的顶点即坐标为整数的点,又称整点)。每秒钟,所有内部白点同时变黑,直到不存在内部白点为止。你的任务是统计最后网格中的黑点个数。 内部白点的定义:一个白色的整点P(x,y)是内部白点当且仅当P在水平线的左边和右边各至少有一个黑点(即存在x1 < x < x2使得(x1,y)和(x2,y)都是黑点),且在竖直线的上边和下边各至少有一个黑点(即存在y1 < y < y2使得(x,y1)和(x,y2)都是黑点)。
Input
输入第一行包含一个整数n,即初始黑点个数。以下n行每行包含两个整数(x,y),即一个黑点的坐标。没有两个黑点的坐标相同,坐标的绝对值均不超过109。
Output
输出仅一行,包含黑点的最终数目。如果变色过程永不终止,输出-1。
Sample Input
4
0 2
2 0
-2 0
0 -2
Sample Output
5
数据范围
36%的数据满足:n < = 500
64%的数据满足:n < = 30000
100%的数据满足:n < = 100000

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(int i = a;i <= n;i++)
#define lv(i,a,n) for(int i = a;i >= n;i--)
#define clean(a) memset(a,0,sizeof(a))
const int INF = 1 << 30;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
    char c;
    bool op = 0;
    while(c = getchar(), c < ‘0‘ || c > ‘9‘)
        if(c == ‘-‘) op = 1;
    x = c - ‘0‘;
    while(c = getchar(), c >= ‘0‘ && c <= ‘9‘)
        x = x * 10 + c - ‘0‘;
    if(op) x = -x;
}
template <class T>
void write(T x)
{
    if(x < 0) putchar(‘-‘), x = -x;
    if(x >= 10) write(x / 10);
    putchar(‘0‘ + x % 10);
}
struct point
{
    int x,y;
}a[100010];
struct seg
{
    int k,x,y,r;
}s[1000010];
int n;
int hsh[100010],cnt = 0;
int tr[100010],ans = 0;
bool cmp1(point a,point b)
{
    if(a.x == b.x)
    return a.y < b.y;
    else
    return a.x < b.x;
}
bool cmp2(point a,point b)
{
    if(a.y == b.y)
    return a.x < b.x;
    else
    return a.y < b.y;
}
bool cmp3(seg a,seg b)
{
    if(a.y == b.y)
    return a.k < b.k;
    else
    return a.y < b.y;
}
int find(int x)
{
    int l = 1,r = n,mid;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(hsh[mid] < x)
        l = mid + 1;
        else if(hsh[mid] > x)
        r = mid - 1;
        else return mid;
    }
}
void insert(int k,int l,int r,int t)
{
    if(!k)
    {
        s[++cnt].x = find(l);
        s[cnt].r = find(r);
        s[cnt].y = t;
    }
    else
    {
        s[++cnt].x = find(t);
        s[cnt].y = l;
        s[cnt].k = 1;
        s[++cnt].x = find(t);
        s[cnt].y = r;
        s[cnt].k = -1;
    }
}
int lowbit(int x)
{
    return x & -x;
}
void build()
{
    sort(a + 1,a + n + 1,cmp1);
    duke(i,2,n)
    {
        if(a[i].x == a[i - 1].x)
        insert(1,a[i - 1].y,a[i].y,a[i].x);
    }
    sort(a + 1,a + n + 1,cmp2);
    duke(i,2,n)
    {
        if(a[i].y == a[i - 1].y)
        insert(0,a[i - 1].x,a[i].x,a[i].y);
    }
}
void update(int x,int y)
{
    while(x <= n)
    {
        tr[x] += y;
        x += lowbit(x);
    }
}
int ask(int x)
{
    int s = 0;
    while(x)
    {
        s += tr[x];
        x -= lowbit(x);
    }
    return s;
}
void work()
{
    duke(i,1,cnt)
    {
        if(!s[i].k)
        ans += ask(s[i].r - 1) - ask(s[i].x);
        else
        update(s[i].x,s[i].k);
    }
}
int main()
{
    read(n);
    duke(i,1,n)
    {
        read(a[i].x);
        read(a[i].y);
        hsh[i] = a[i].x;
    }
    sort(hsh + 1,hsh + n + 1);
    build();
    sort(s + 1,s + cnt + 1,cmp3);
    work();
    printf("%d\n",ans + n);
    return 0;
}
/*
4
0 2
2 0
-2 0
0 -2
*/

原文地址:https://www.cnblogs.com/DukeLv/p/9753792.html

时间: 2024-11-08 04:23:46

B1818 [Cqoi2010]内部白点 树状数组的相关文章

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

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

bzoj 1818 [CQOI 2010] 内部白点 - 扫描线 - 树状数组

题目传送门 快速的列车 慢速的列车 题目大意 一个无限大的方格图内有$n$个黑点.问有多少个位置上下左右至少有一个黑点或本来是黑点. 扫描线是显然的. 考虑一下横着的线段,取它两个端点,横坐标小的地方放一个+1,大的地方放一个-1事件. 然后扫描,扫到的横着的线段更新,竖着的线段用树状数组求答案. 然后考虑这一列上原来存在的黑点有没有被统计,如果没有就加上. Code 1 /** 2 * bzoj 3 * Problem#1818 4 * Accepted 5 * Time: 1824ms 6

区间的关系的计数 HDU 4638 离线+树状数组

题目大意:给你n个人,每个人都有一个id,有m个询问,每次询问一个区间[l,r],问该区间内部有多少的id是连续的(单独的也算是一个) 思路:做了那么多离线+树状数组的题目,感觉这种东西就是一个模板了,23333,反正都是定义右区间的. 这题的关键难度就是如何定义id是连续的呢.我们每次往区间里面放一个数值以后都要add(pos, 1),就是把pos~n的所有的关系都+1.然后如果说在pos之前就出现id-1,就要add(pos[id-1], -1)(同理id+1也是这样),这样子表示从pos[

【树状数组】Codeforces Round #755 D. PolandBall and Polygon

http://codeforces.com/problemset/problem/755/D 每次新画一条对角线的时候,考虑其跨越了几条原有的对角线. 可以用树状数组区间修改点查询来维护多边形的顶点.答案每次增加 新对角线的左端点在多少个区间内+右端点在多少个区间内+1,每次把新画的对角线所覆盖的较小区间内部的每个点加1即可.注意,一定要是较小的区间,因为这样才能保证其左右端点不同时在区间内,不会重复统计. #include<cstdio> #include<algorithm>

树状数组和线段树

一.树状数组 在解题过程中,我们有时需要维护一个数组的前缀和 S[i]=A[1]+A[2]+...+A[i] .但是不难发现,如果我们修改了任意一个 A[i],S[i] . S[i+1]...S[n] 都会发生变化.可以说,每次修改 A[i] 后,调整前缀和 S[] 在最坏情况下会需要 O(n) 的时间.当 n 非常大时,程序会运行得非常缓慢.因此,这里我们引入"树状数组",它的修改与求和都是 O(logn) 的,效率非常高. 实现: 对于正整数x,定义lowbit(x)为x的二进制表

树状数组的基本用法

树状数组的基本用法和奇技淫巧 树状数组是一种修改查找复杂度都是logN的实用的数据结构,大家应该都会,下面放一张熟的不能再熟的图装装样子 树状数组最基本的操作:单点修改,前缀查询.原理都懂就不赘述了,贴个代码. void add(int pos,int x) { for(;pos<=N;pos+=lowbit(pos)) c[pos]+=x; } int query(int pos) { int sum=0; for(;pos;pos-=lowbit(pos)) sum+=c[pos]; ret

树状数组(Binary Indexed Tree,BIT)

树状数组(Binary Indexed Tree) 前面几篇文章我们分享的都是关于区间求和问题的几种解决方案,同时也介绍了线段树这样的数据结构,我们从中可以体会到合理解决方案带来的便利,对于大部分区间问题,线段树都有其绝对的优势,今天这篇文章,我们就来欣赏由线段树变形的另外一个数据结构--树状数组,树状数组通常也用于解决区间求和.单点更新的问题,而且效率比线段树高一些(树状数组区间求和和单点更新的时间复杂度均为o(log n)),相对而言,线段树的应用范围可能更广泛一些.但不得不承认,树状数组确

【HDOJ 5654】 xiaoxin and his watermelon candy(离线+树状数组)

pid=5654">[HDOJ 5654] xiaoxin and his watermelon candy(离线+树状数组) xiaoxin and his watermelon candy Time Limit: 4000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 233    Accepted Submission(s): 61 Problem Des

codeforcesRound378C-dfs+树状数组

分成K个块,每个块内部dfs解决,然后用树状数组统计第i个元素前面有多少怪物已经消失,来计算当前的下标 1 #include<bits/stdc++.h> 2 3 #define inf 0x3f3f3f3f 4 5 const int maxn=1000; 6 7 using namespace std; 8 9 typedef pair<int,char> P; 10 11 vector<P> G[maxn+10]; 12 13 int n; 14 15 int k