POJ 2528 Mayor's posters(线段树染色+离散化)

来源:互联网 发布:淘宝网上开店要钱吗 编辑:程序博客网 时间:2024/05/21 17:12

题目来源:POJ
题意:在墙上贴海报,后贴的会覆盖之前的,问最后能看见几张海报

线段树染色问题,注意题目里每个数字代表一个单位长度而不是点,普通离散化会出现问题。以下例子来自NotOnlySuccess菊苣的博客
例子一:1-10 1-4 5-10
例子二:1-10 1-4 6-10
普通离散化后都变成了[1,4][1,2][3,4]
线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢?
例子一是完全被覆盖掉了,而例子二没有被覆盖”

针对这个问题,网上常见的做法是对离散化的数组做一个小处理。以下同样来自NotOnlySuccess菊苣的博客
”为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.“
但是,这样做会增加maxn的大小……令人难以忍受……

事实上,只要在输入[l, r]的时候进行处理,变成[l, r + 1],相当于线段树存储的是一整段区间的信息(传说中的“段树”),就可以应用普通离散化了,具体见代码

另外,由于POJ这题数据奇弱无比,一种叫做浮水法的n^2的做法也是可以过的,这个是我搜本题题解的时候搜到的一种做法,网上资料很少,下面同样附上代码和本人的理解o( ̄▽ ̄)ブ
浮水法:本题中,将每条线段和其后的线段进行比较,没有重叠部分就继续和下一条比较,有重叠部分则去掉重叠部分再继续比较,比较是递归的。这个递归过程就像一层一层上浮最后浮到水面上一样,所以叫浮水法(联想到冒泡)。如果某条线段最后能浮到水面上,ans++

// 线段树#include <algorithm>#include <cctype>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <map>#include <queue>#include <string>#define ALL(v) v.begin(), v.end()#define CLR(array) memset(array, 0, sizeof(array))#define DEBUG cout << "bug "#define Fr first#define FOR(i, n) for(int i = 0; i < n; i++)#define MEM(array) memset(array, 0x3f, sizeof(array))#define ls L(id)#define lson id << 1, l, mid#define OFF(array) memset(array, -1, sizeof(array))#define PR(x) cout << #x << " = " << x << "  "#define PRLN(x) cout << #x << " = " << x << endl#define rep(i, a, b) for(int i = a; i <= b; i++)#define root 1, 1, n#define rs R(id)#define rson id << 1 | 1, mid, r // 注意这里是 mid#define Rep(i, a, b) for(int i = a; i >= b; i--)#define repk(i, a, b) for(i = a; i <= b; i++)#define Repk(i, a, b) for(i = a; i >= b; i--)#define Sc second#define SZ size()using namespace std;inline int L(int x){ return x << 1; }inline int R(int x){ return x << 1 | 1; }inline int MID(int l, int r){ return l + r >> 1; }typedef long long ll;typedef pair<int, int> P;const int inf = 0x3f3f3f3f, maxn = 2e4 + 5;int color[maxn << 2];int x[maxn >> 1], y[maxn >> 1];int vis[maxn >> 1];void push_down(int id) {    color[ls] = color[rs] = color[id];    color[id]= -1;}void update(int id, int l, int r, int a, int b, int val) {    if(a <= l && r <= b) {        color[id] = val;        return;    }    if(r > l && ~color[id]) push_down(id);    int mid = MID(l, r);    if(mid > a) update(lson, a, b, val);    //第一次写段树,一开始总是莫名崩溃,后来发现此处的判断跟点树不一样,不应该有等号,否则会无限递归 -> 越界    if(mid < b) update(rson, a, b, val);}void query(int id, int l ,int r) {    if(~color[id]) {        vis[color[id]] = 1;        return;    }    if(l + 1 == r) return;    int mid = MID(l, r);    query(lson);    query(rson);}int compress(int m) { // 离散化部分    vector<int> v;    FOR(i, m) {        scanf("%d%d", x + i, y + i);        y[i]++; // 此处 +1, 使 x[i], y[i] 表示区间的左右端点        v.push_back(x[i]);        v.push_back(y[i]);    }    sort(ALL(v));    v.resize(unique(ALL(v)) - v.begin());    FOR(i, m) {        x[i] = 1 + lower_bound(ALL(v), x[i]) - v.begin();        y[i] = 1 + lower_bound(ALL(v), y[i]) - v.begin();    }    return v.SZ;}int solve(void) {    int n, m, ans = 0;     scanf("%d", &m);    n = compress(m);    color[1] == m; //开始全部无色,用 m 表示    FOR(i, m) update(root, x[i], y[i], i);    CLR(vis);    query(root);     FOR(i, m) if(vis[i]) ans++;    return ans;}int main(void) {    freopen("F:\\Desktop\\in.txt", "r", stdin);    int t; scanf("%d", &t);    while(t--) printf("%d\n", solve());    return 0;}
// 浮水法// 此处省略头文件50行const int inf = 0x3f3f3f3f, maxn = 1e4 + 5;int a[maxn], b[maxn];int n;int cover(int l, int r, int id) {    if(id > n) return 1;    int nowl = a[id], nowr = b[id];    if(l > nowr || r < nowl) return cover(l, r, id + 1);    if(r > nowr && cover(nowr + 1, r, id + 1)) return 1;    if(nowl > l && cover(l, nowl - 1, id + 1)) return 1;    return 0;}int solve(void) {    int ans = 0;     scanf("%d", &n);    rep(i, 1, n) scanf("%d%d", a + i, b + i);    rep(i, 1, n)         if(cover(a[i], b[i], i + 1)) ans++;    return ans;}int main(void) {    freopen("F:\\Desktop\\in.txt", "r", stdin);    int t; scanf("%d", &t);    while(t--) printf("%d\n", solve());    return 0;}
0 0
原创粉丝点击