1. 程式人生 > >zoj 3299(區間改動+離散化)

zoj 3299(區間改動+離散化)

boa tor com 繪圖 string 水過 mes ont popu

題意:有n個由小木塊組成的長條木塊要掉下來。給出木塊的左右區間,然後有給了m個木板的左右區間和高度用來接住木塊,由於木塊是由小木塊接觸組成的,也就是木板能夠接住一部分的木塊。剩下的會繼續掉落,問最後每一個木板上有多少個小木塊。
題解:這道題用線段樹可解,還有還有一個比較機智的做法。
先說線段樹,左右區間到3×1e7,假設用線段樹解決須要離散化。

把木板從低到高排序後用一個線段樹flag維護每一個區間相應的木板編號。這樣高的木板就能夠覆蓋低木板的編號,然後用還有一個線段樹sum維護每一個長條木塊在每一個區間內的數量。由於有可能n個長條木塊區間有重疊。所以一個區間內不會僅僅有一排小木塊,那麽每更新一個長條木塊,相應的區間的長條木塊數量加1。

最後查詢每一個區間全部的小木塊數量就是相應的長條木塊數量乘區間長度。一開始一直MLE,然後把離散用的map換成二分查找就水過去了。
下面是線段樹做法的代碼:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 100001;
struct Board {
    int l, r, h, id;
}boa[N];
int n, m, l[N], r[N];
long long res[N];
int
sum[N << 4], flag[N << 4]; vector<int> a; bool cmp(const Board& a, const Board& b) { return a.h < b.h; } void pushdown(int k) { if (flag[k]) { flag[k * 2] = flag[k * 2 + 1] = flag[k]; flag[k] = 0; } if (sum[k]) { sum[k * 2] += sum[k]; sum[k * 2
+ 1] += sum[k]; sum[k] = 0; } } void modify1(int k, int left, int right, int l1, int r1, int x) { if (l1 <= left && right <= r1) { flag[k] = x; return; } pushdown(k); int mid = (left + right) / 2; if (l1 < mid) modify1(k * 2, left, mid, l1, r1, x); if (r1 > mid) modify1(k * 2 + 1, mid, right, l1, r1, x); } void modify2(int k, int left, int right, int l1, int r1) { if (l1 <= left && right <= r1) { sum[k]++; return; } pushdown(k); int mid = (left + right) / 2; if (l1 < mid) modify2(k * 2, left, mid, l1, r1); if (r1 > mid) modify2(k * 2 + 1, mid, right, l1, r1); } void query(int k, int left, int right) { if (flag[k]) { res[flag[k]] += (long long)sum[k] * (a[right] - a[left]); return; } if (left + 1 == right) return; pushdown(k); int mid = (left + right) / 2; query(k * 2, left, mid); query(k * 2 + 1, mid, right); } int main() { while (scanf("%d%d", &n, &m) == 2) { a.clear(); memset(sum, 0, sizeof(sum)); memset(flag, 0, sizeof(flag)); memset(res, 0, sizeof(res)); for (int i = 1; i <= n; i++) { scanf("%d%d", &l[i], &r[i]); a.push_back(l[i]); a.push_back(r[i]); } for (int i = 1; i <= m; i++) { scanf("%d%d%d", &boa[i].l, &boa[i].r, &boa[i].h); boa[i].id = i; a.push_back(boa[i].l); a.push_back(boa[i].r); } sort(a.begin(), a.end()); a.erase(unique(a.begin(), a.end()), a.end()); int cnt = a.size(); sort(boa + 1, boa + 1 + m, cmp); for (int i = 1; i <= m; i++) { int pos1 = lower_bound(a.begin(), a.end(), boa[i].l) - a.begin(); int pos2 = lower_bound(a.begin(), a.end(), boa[i].r) - a.begin();; modify1(1, 0, cnt - 1, pos1, pos2, boa[i].id); } for (int i = 1; i <= n; i++) { int pos1 = lower_bound(a.begin(), a.end(), l[i]) - a.begin(); int pos2 = lower_bound(a.begin(), a.end(), r[i]) - a.begin();; modify2(1, 0, cnt - 1, pos1, pos2); } query(1, 0, cnt - 1); for (int i = 1; i <= m; i++) printf("%lld\n", res[i]); printf("\n"); } return 0; }

還有一種做法是看別人的題解的,速度又快代碼又短(就喜歡寫代碼短的╮(╯▽╰)╭),不須要離散化。把全部木塊和木板所在區間從左到右掃描一邊,結合繪圖非常好理解。

#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
const int N = 100005;
struct Board {
    int h, l, r;
    long long num;
}boa[N << 2];
struct Node {
    int st, id, v;//區間起點。區分木塊還是木板。區分左右端點
}node[N << 2];
int n, m;
map<int,int> mp;//存當前處理區間內全部的木板,左值是木板高度,右值是木板相應編號

bool cmp(const Node& a, const Node& b) {
    if (a.st != b.st)
        return a.st < b.st;
    return a.v < b.v;
}

int main() {
    while (scanf("%d%d", &n, &m) == 2) {
        mp.clear();
        int cnt = 0, l, r;
        for (int i = 1; i <= n; i++) {
            scanf("%d%d", &l, &r);
            node[++cnt].st = l, node[cnt].id = 0, node[cnt].v = 1;
            node[++cnt].st = r, node[cnt].id = 0, node[cnt].v = -1;
        }
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &boa[i].l, &boa[i].r, &boa[i].h);
            boa[i].num = 0;
            node[++cnt].st = boa[i].l, node[cnt].id = i, node[cnt].v = 1;
            node[++cnt].st = boa[i].r, node[cnt].id = i, node[cnt].v = -1;
        }
        sort(node + 1, node + 1 + cnt, cmp);
        int pre = node[0].st, cnt1 = 0;//當前處理區間起點,當前區間的長條木塊數量
        for (int i = 1; i <= cnt; i++) {
            if (mp.rbegin() != mp.rend()) {//map自己主動排序。所以map裏最後一個值一定是最大的。也就是最高的木板
                int id = mp.rbegin() -> second;//右值是編號
                boa[id].num += (long long)(node[i].st - pre) * cnt1;//小木塊數量是長條木塊乘區間長度
            }
            if (!node[i].id) {//當前處理區間是長條木塊
                if (node[i].v == 1)
                    cnt1++;//長條木塊數量加1
                else
                    cnt1--;
            }
            else {//木板
                if (node[i].v == 1)//左端點增加木板
                    mp[boa[node[i].id].h] = node[i].id;
                else//右端點去掉木板
                    mp.erase(boa[node[i].id].h);
            }
            pre = node[i].st;//更新處理區間起點
        }
        for (int i = 1; i <= m; i++)
            printf("%lld\n", boa[i].num);
        printf("\n");
    }
    return 0;
}

zoj 3299(區間改動+離散化)