二维数点
理解起来并不简单,很坎坷吧。但是理解了是真会发出感叹
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;
}