扫描线、权值线段树

二维数点

理解起来并不简单,很坎坷吧。但是理解了是真会发出感叹

focus:

        互斥理论

        vx存的是各个点的x坐标,后期用来离散化

        vector<array<int, 4>> event   表示事件, 插入一个点  是类型不同的事件

        使用扫描线的动态思想,要将优先将y进行排序,其次是事件的类型和x,最后是查询的编号。排序时,事件的类型和x在本题可以互换顺序(已测),数组各位存放的数据要根据你对排序的要求进行存放。  array的排序和pair类似, 都是先比较第一个,然后第二个....

        

// problem : 互斥、离散化、树状数组 

#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
const int N = 2e5 + 5;
int n, q, m, c[N], ans[N];
std::vector<int> vx;
vector<array<int, 4>> event;
void modify(int x, int d) {
    for(; x <= m; x += x & -x)
        c[x] += d;
}
int query(int x) {
    int res = 0;
    for(; x; x -= x & -x) 
        res += c[x];
    return res;
}
int main(){
    scanf("%d %d", &n, &q);
    for(int i = 1; i <= n; ++i) {
        int x, y; scanf("%d %d", &x, &y);
        vx.push_back(x);
        event.push_back({y, 0, x});
    }

    for(int i = 1; i <= q; ++i) {
        int x1, x2, y1, y2;
        scanf("%d %d %d %d", &x1, &x2, &y1, &y2);
        event.push_back({y2, 1, x2, i});   // 事件类型  1:加法  2:减法(可以互换)
        event.push_back({y1 - 1, 1, x1 - 1, i});  // 但插入一定要是0 (优先级高)
        event.push_back({y2, 2, x1 - 1, i});
        event.push_back({y1 - 1, 2, x2, i});
    }

    sort(vx.begin(), vx.end());
    vx.erase(unique(vx.begin(), vx.end()), vx.end());
    sort(event.begin(), event.end());

    m = vx.size();   // vx离散化后,只有m个点,那么坐标为[1,m] ,树状数组大小就为m

    for(auto evt : event) {
        if(evt[1] == 0) {
            int pos = lower_bound(vx.begin(), vx.end(), evt[2]) - vx.begin() + 1;
            modify(pos, 1);
        } else {
            int pos = upper_bound(vx.begin(), vx.end(), evt[2]) - vx.begin();
            int tmp = query(pos);
            if(evt[1] == 1)ans[evt[3]] += tmp;
            else ans[evt[3]] -= tmp;
        }
    }

    for(int i = 1; i <= q; ++i)
        printf("%d\n", ans[i]);

    return 0;
}

矩形面积并

这题也实在是巧妙%%%

正如    线段树    该篇博客所提,线段树可以用来维护区间的最小值以及最小值的个数。

所以,我们可以利用线段树维护每个区间被覆盖的最小次数,以及对应的覆盖长度。如果被覆盖的次数为0,则说明这一段区间没有被覆盖。利用 总的最大覆盖长度 - 没有被覆盖的长度     也就是此刻的覆盖长度。求得覆盖的长度,再乘以前后两个事件的y的差值,即求得了改事件所贡献的矩阵面积。

 比如线段树  n 个点  a1 ... an    最后划分成了n个区间    build(1, 1, n)    

我们有vx.size()个点,   vx.size() - 1   个区间  

// problem :  

#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
const int N = 2E5 + 5;
std::vector<int > vx;
vector<array<int, 4>> event;
int n, m;
struct info {
    int minv, mincnt;
};

info operator + (const info &l, const info &r) {
    info a;
    a.minv = min(l.minv, r.minv);
    if(l.minv == r.minv) a.mincnt = l.mincnt + r.mincnt;
    else if (l.minv < r.minv) a.mincnt = l.mincnt;
    else a.mincnt = r.mincnt;
    return a;
}
struct node {
    int t;
    info val;
}seg[N * 8];

void update(int id) {
    seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}

void settag(int id, int t) {
    seg[id].val.minv += t;
    seg[id].t += t;
}

void pushdown(int id) {
    if(seg[id].t) {
        settag(id * 2, seg[id].t);
        settag(id * 2 + 1, seg[id].t);
        seg[id].t = 0;
    }
}

void build(int id, int l, int r) {
    if(l == r) {
        seg[id].val = {0, vx[r] - vx[r - 1]};
    } else {
        int mid = (l + r) / 2;
        build(id * 2, l, mid);
        build(id * 2 + 1, mid + 1, r);
        update(id);
    }
}

void modify(int id, int l, int r, int ql, int qr, int t) {
    if(l == ql && r == qr) {
        settag(id, t);
        return;
    }
    int mid = (l + r) / 2;
    pushdown(id);
    if(qr <= mid) modify(id * 2, l, mid, ql, qr, t);
    else if(ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, t);
    else modify(id * 2, l, mid, ql, mid, t),
        modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
    update(id);
}
int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        int x1, x2, y1, y2;
        scanf("%d %d %d %d", &x1, &x2, &y1, &y2);
        vx.push_back(x1);
        vx.push_back(x2);
        event.push_back({y1, 1, x1, x2});
        event.push_back({y2, -1, x1, x2});
    }

    sort(event.begin(), event.end());
    sort(vx.begin(), vx.end());
    vx.erase(unique(vx.begin(), vx.end()), vx.end());

    m = vx.size() - 1;
    build(1, 1, m);

    int totlen = seg[1].val.mincnt;
    int prey = 0;
    ll ans = 0;
    for(auto evt : event) {
        int cov = totlen;
        if(seg[1].val.minv == 0)
            cov = totlen - seg[1].val.mincnt;
        ans += (ll)(evt[0] - prey) * cov;
        prey = evt[0];
        
        int x1 = lower_bound(vx.begin(), vx.end(), evt[2]) - vx.begin() + 1;
        int x2 = lower_bound(vx.begin(), vx.end(), evt[3]) - vx.begin();
        // int x2 = lower_bound(vx.begin(), vx.end(), evt[3]) - vx.begin() + 1 - 1;
        // 为什么要减1呢? 跟 m = vx.size() - 1   是相同的原因 
        if(x1 > x2)continue;
        modify(1, 1, m, x1, x2, evt[1]);
    }
    printf("%lld\n", ans);

    return 0;
}

区间不同数的和

// problem :  

#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
const int N = 2E5 + 5;
ll n, q, a[N], pos[N];
ll c[N], ans[N];
std::vector<PII> qu[N];

void modify(int x, ll d) {
    for(; x <= n; x += x & -x)
        c[x] += d;
}

ll query(int x) {
    ll res = 0;
    for(; x; x -= x & -x)
        res += c[x];
    return res;
}
int main(){
    scanf("%lld %lld", &n, &q);
    for(int i = 1; i <= n; ++i)
        scanf("%lld", &a[i]);

    for(int i = 1; i <= q; ++i) {
        int l, r;
        scanf("%d %d", &l, &r);
        qu[r].push_back({l, i});
    }   

    for(int r = 1; r <= n; ++r) {
        int p = pos[a[r]];
        modify(p + 1, a[r]);
        modify(r + 1, -a[r]);
        pos[a[r]] = r;

        for(auto que : qu[r]) 
            ans[que.second] = query(que.first);
        
    }
    for(int i = 1; i <= q; ++i) {
        printf("%lld\n", ans[i]);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xingxg.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值