bzoj1818: [Cqoi2010]内部白点

题面在这里

吐槽:现在做题再也不能想也不想就看题解了!!!QAQ!!否则我可能就没有未来了QAQ
好了不要管上面的吐槽qaq

题意:

平面上有n个黑点,剩下的整点全部是白点。
一个白点如果上下左右各至少有一个黑点就称内部白点。
现在每一次操作可以将所有的内部白点变成黑点,一直操作到不能操作为止。
问最后有多少的黑点。如果无法停止操作输出-1.
n<=105,109 n <= 10 5 , 坐 标 范 围 10 9

做法:

首先这个题面里很多东西都是假的qwq…没有-1的情况,而且最多只会操作一次。(显然)
于是我们发现要求的东西就是横线与竖线的交叉点有多少个。
然后就扫描线搞搞的事了qwq..

要注意的是,由于交叉点不能算某些原来就有的点,所以对于一个横坐标,假设这个横坐标上有 k k 个点,它们的纵坐标分别是y1,y2,...,yk,则需要分成 k1 k − 1 个线段插入,且线段i的左右端点分别是 yi+1,yi+11 y i + 1 , y i + 1 − 1 。纵坐标同理。

代码:

/*************************************************************
    Problem: bzoj 1818 [Cqoi2010]内部白点
    User: fengyuan
    Language: C++
    Result: Accepted
    Time: 1912 ms
    Memory: 12028 kb
    Submit_Time: 2018-01-31 18:09:23
*************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<vector>
#define pb push_back
#define mp make_pair
#define F first
#define S second
#define lc o<<1
#define rc o<<1|1
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;

inline ll read() {
    char ch = getchar(); ll x = 0; int op = 1;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
    for(; isdigit(ch); ch = getchar()) x = x*10+ch-'0';
    return x*op;
}
inline void write(ll a) {
    if(a < 0) putchar('-'), a = -a;
    if(a >= 10) write(a/10); putchar('0'+a%10);
}

const int N = 100010;
int n, cx, cy, ans;
int xx[N], yy[N], t[N];
struct node { int x, y; }p[N];
vector<pii> tg[N], l[N];
vector<int> rp[N], cp[N];

inline void add(int k, int v) { for(int i = k; i <= cy; i += i&-i) t[i] += v; }
inline int sum(int k) { int ret = 0; for(int i = k; i; i -= i&-i) ret += t[i]; return ret; }
int main() {
    n = read();
    for(int i = 1; i <= n; i ++) {
        p[i].x = read(); p[i].y = read();
        xx[++ cx] = p[i].x; yy[++ cy] = p[i].y;
    } sort(xx+1, xx+1+cx); sort(yy+1, yy+1+cy);
    cx = unique(xx+1, xx+1+cx)-xx-1; cy = unique(yy+1, yy+1+cy)-yy-1;
    for(int i = 1; i <= n; i ++) {
        int x = lower_bound(xx+1, xx+1+cx, p[i].x)-xx, y = lower_bound(yy+1, yy+1+cy, p[i].y)-yy;
        rp[x].pb(y); cp[y].pb(x);
    }
    for(int i = 1; i <= cx; i ++) sort(rp[i].begin(), rp[i].end());
    for(int i = 1; i <= cy; i ++) sort(cp[i].begin(), cp[i].end());
    for(int i = 1; i <= cx; i ++)
        if((int)rp[i].size() > 1) {
            for(int j = 1; j < (int)rp[i].size(); j ++) l[i].pb(mp(rp[i][j-1]+1, rp[i][j]-1));
        }
    for(int i = 1; i <= cy; i ++)
        if((int)cp[i].size() > 1) {
            for(int j = 1; j < (int)cp[i].size(); j ++) {
                tg[cp[i][j-1]+1].pb(mp(i, 1));
                tg[cp[i][j]].pb(mp(i, -1));
            }
        }
    for(int i = 1; i <= cx; i ++) {
        for(int j = 0; j < (int)tg[i].size(); j ++) add(tg[i][j].F, tg[i][j].S);
        for(int j = 0; j < (int)l[i].size(); j ++) {
            ans += sum(l[i][j].S)-sum(l[i][j].F-1);
        }
    }
    write(ans + n); puts("");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值