P4390 [BalkanOI2007] Mokia 摩基亚 / (离线)简单题
第一眼看题,emmm,跟分治有半毛钱关系啊!!!!这每次分治一次不直接复杂度爆炸??
冷静下来后,我们发现对于一个点,对于区间产生贡献充要条件是他的 \(x\) 轴要在区间内。
所以。。。我们是不是可以离线下来对于 \(x\) 轴排一遍序,我们只有左半部分的点会对右半部分产生贡献。
而且我们可以对于一次询问的左下角和右上角拆成两个点,右上角的前缀和减去左下角的前缀和就是答案。
用之前三维偏序的方法处理 \(y\) 轴就行了。
HOI4
#include <bits/stdc++.h>
class Ask {
public:
int opt, x[2], y[2], a, id;
}number[200000];
int N, W, tot;
int flag[210000];
int answer[210000];
class Position1 {
public:
int x, y, val;
}pos[210000];
class Position2 {
public:
int x, y1, y2, id;
}temp[210000];
class Binary_indexed_tree {
#define lowbit(x) (x & (-x))
public:
int number[2100000];
void Insert(int position, int val) {
while (position <= W) {
number[position] += val;
position += lowbit(position);
}
}
int Query(int position) {
int result = 0;
while (position) {
result += number[position];
position -= lowbit(position);
}
return result;
}
}tree;
bool cmp1(const Position1 &a, const Position1 &b) {
return a.x < b.x;
}
bool cmp2(const Position2 &a, const Position2 &b) {
return a.x < b.x;
}
void CDQ(int l, int r) {
if (l == r)
return;
int mid = (l + r) >> 1;
CDQ(l, mid);
CDQ(mid + 1, r);
int top1 = 0, top2 = 0;
for (int i = l; i <= mid; ++ i)
if (number[i].opt == 1)
pos[++ top1] = {number[i].x[0], number[i].y[0], number[i].a};
for (int i = mid + 1; i <= r; ++ i) {
if (number[i].opt == 2) {
temp[++ top2] = {number[i].x[0] - 1, number[i].y[0], number[i].y[1], number[i].id};
temp[++ top2] = {number[i].x[1], number[i].y[0], number[i].y[1], number[i].id};
}
}
std::sort(pos + 1, pos + 1 + top1, cmp1);
std::sort(temp + 1, temp + 1 + top2, cmp2);
int I = 1, J = 1;
while (I <= top1 && J <= top2) {
if (pos[I].x <= temp[J].x) {
tree.Insert(pos[I].y, pos[I].val);
I ++;
}
else {
int ID = temp[J].id;
answer[ID] += flag[ID] * (tree.Query(temp[J].y2) - tree.Query(temp[J].y1 - 1));
flag[ID] *= -1;
J ++;
}
}
while (J <= top2) {
int ID = temp[J].id;
answer[ID] += flag[ID] * (tree.Query(temp[J].y2) - tree.Query(temp[J].y1 - 1));
flag[ID] *= -1;
J ++;
}
for (int i = 1; i <= I - 1; ++ i)
tree.Insert(pos[i].y, -pos[i].val);
}
int cnt;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
memset(flag, -1, sizeof(flag));
std::cin >> W >> W;
std::cin >> number[++ tot].opt;
while (number[tot].opt != 3) {
if (number[tot].opt == 1) {
std::cin >> number[tot].x[0] >> number[tot].y[0] >> number[tot].a;
}
else {
number[tot].id = ++ cnt;
std::cin >> number[tot].x[0] >> number[tot].y[0] >> number[tot].x[1] >> number[tot].y[1];
}
std::cin >> number[++ tot].opt;
}
N = tot - 1;
tot = 0;
CDQ(1, N);
for (int i = 1; i <= cnt; ++ i)
std::cout << answer[i] << '\n';
return 0;
}
P4169 [Violet] 天使玩偶/SJY摆棋子
把绝对值拆成 \(4\) 部分,对于每一个询问都有左上,左下,右上,右下的点来维护。
好麻烦啊。。cdq都这么麻烦吗QAQ。
EU4
#include <bits/stdc++.h>
const int end = 1e6 + 10;
int N, M, cnt;
int answer[501000];
class Position {
public:
int x, y, opt, id;
}ask[1010000], pos[1010000];
class Binary_Inexed_Tree {
#define lowbit(x) (x & (-x))
public:
int min[2010000];
Binary_Inexed_Tree() {
for (int i = 1; i <= 2 * end + 10; ++ i)
min[i] = 1e9;
}
void clear(int position) {
position += end + 10;
while (position <= 2 * end + 10) {
if (min[position] == 1e9)
return;
min[position] = 1e9;
position += lowbit(position);
}
}
void Insert(int position, int val) {
position += end + 10;
while (position <= 2 * end + 10) {
if (min[position] <= val)
return;
min[position] = std::min(min[position], val);
position += lowbit(position);
}
}
int Query(int position) {
int result = 1e9;
position += end + 10;
while (position) {
result = std::min(result, min[position]);
position -= lowbit(position);
}
return result;
}
#undef lowbit(x)
}leftUp, leftDown;
bool cmp(const Position &a, const Position &b) {
return a.x < b.x;
}
void CDQ(int l, int r) {
if (l == r)
return;
int mid = (l + r) >> 1;
int I = l, J = mid + 1;
int beginToNow, endToNow, x, y, id;
CDQ(l, mid);
CDQ(mid + 1, r);
while (I <= mid && J <= r) {
if (pos[I].opt == 2) {
I ++;
continue;
}
if (pos[J].opt == 1) {
J ++;
continue;
}
if (pos[I].x <= pos[J].x) {
beginToNow = pos[I].y;
endToNow = end - pos[I].y + 1;
x = pos[I].x, y = pos[I].y;
leftUp.Insert(endToNow, -x + y);
leftDown.Insert(beginToNow, -x - y);
I ++;
}
else {
beginToNow = pos[J].y;
endToNow = end - pos[J].y + 1;
x = pos[J].x, y = pos[J].y, id = pos[J].id;
int temp = leftUp.Query(endToNow);
if (temp != 1000000000ll)
answer[id] = std::min(answer[id], x - y + temp);
temp = leftDown.Query(beginToNow);
if (temp != 1000000000ll)
answer[id] = std::min(answer[id], x + y + temp);
J ++;
}
}
while (J <= r) {
if (pos[J].opt == 1) {
J ++;
continue;
}
beginToNow = pos[J].y;
endToNow = end - pos[J].y + 1;
x = pos[J].x, y = pos[J].y, id = pos[J].id;
int temp = leftUp.Query(endToNow);
if (temp != 1000000000ll)
answer[id] = std::min(answer[id], x - y + temp);
temp = leftDown.Query(beginToNow);
if (temp != 1000000000ll)
answer[id] = std::min(answer[id], x + y + temp);
J ++;
}
for (int i = l; i <= I; ++ i) {
leftUp.clear(end - pos[i].y + 1);
leftDown.clear(pos[i].y);
}
std::inplace_merge(pos + l, pos + mid + 1, pos + r + 1, cmp);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> N >> M;
for (int i = 1; i <= N; ++ i) {
std::cin >> ask[i].x >> ask[i].y;
ask[i].opt = 1;
}
for (int i = N + 1; i <= N + M; ++ i) {
std::cin >> ask[i].opt;
std::cin >> ask[i].x >> ask[i].y;
if (ask[i].opt == 2) {
ask[i].id = ++ cnt;
answer[cnt] = 1e9;
}
}
for (int i = 1; i <= N + M; ++ i) {
pos[i].x = ask[i].x;
pos[i].y = ask[i].y;
pos[i].opt = ask[i].opt;
pos[i].id = ask[i].id;
}
CDQ(1, N + M);
for (int i = 1; i <= N + M; ++ i) {
pos[i].x = end - ask[i].x;
pos[i].y = end - ask[i].y;
pos[i].opt = ask[i].opt;
pos[i].id = ask[i].id;
}
CDQ(1, N + M);
for (int i = 1; i <= cnt; ++ i) {
std::cout << answer[i] << '\n';
}
return 0;
}
/*
2 3
1 1
2 3
2 1 2
1 3 3
2 4 2
*/
[CQOI2011] 动态逆序对
这个还是比较好想的,我们可以处理出来每次删除点 \(i\) 时,前面还剩下的数有多少个数比 \(i\) 位置的数大,后面还剩下的数有多少个比 \(i\) 位置的小。
这样我们就能求出每次删除减少的逆序对了,然后就知道答案了。
Skyline
#include <bits/stdc++.h>
int N, M;
int answer[51000];
class Ask {
public:
int tim, pos, val;
}ask[51000], transfer[51000];
class Binary_indexed_Tree {
#define lowbit(x) (x & (-x))
public:
int number[110000];
void Add(int position, int val) {
while (position <= N) {
number[position] += val;
position += lowbit(position);
}
}
int Query(int position) {
int result = 0;
while (position) {
result += number[position];
position -= lowbit(position);
}
return result;
}
void clear(int position) {
while (position <= N) {
number[position] = 0;
position += lowbit(position);
}
}
}tree;
int barrel[110000];
bool cmp(const Ask &a, const Ask &b) {
return a.pos < b.pos;
}
void CDQ(int l, int r) {
if (l == r)
return;
int mid = (l + r) >> 1;
int I = l, J = mid + 1;
CDQ(l, mid);
CDQ(mid + 1, r);
while (I <= mid && J <= r) {
if (ask[I].pos <= ask[J].pos) {
tree.Add(ask[I].val, 1);
I ++;
}
else {
answer[ask[J].tim] -= tree.Query(N) - tree.Query(ask[J].val);
J ++;
}
}
while (J <= r) {
answer[ask[J].tim] -= tree.Query(N) - tree.Query(ask[J].val);
J ++;
}
for (int i = l; i <= I - 1; ++ i)
tree.clear(ask[i].val);
std::inplace_merge(ask + l, ask + mid + 1, ask + r + 1, cmp);
}
int temp[110000], val[110000];
long long sum = 0;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> N >> M;
for (int i = 1; i <= N; ++ i) {
std::cin >> val[i];
barrel[val[i]] = i;
tree.Add(val[i], 1);
temp[i] += tree.Query(N) - tree.Query(val[i]);
sum += temp[i];
}
for (int i = 1; i <= N; ++ i)
tree.clear(i);
for (int i = N; i >= 1; -- i) {
temp[i] += tree.Query(val[i]);
tree.Add(val[i], 1);
}
for (int i = 1; i <= N; ++ i)
tree.clear(i);
for (int i = 1; i <= M; ++ i) {
std::cin >> transfer[i].val;
transfer[i].tim = i;
transfer[i].pos = barrel[transfer[i].val];
answer[i] = temp[transfer[i].pos];
ask[i] = transfer[i];
}
CDQ(1, M);
for (int i = 1; i <= M; ++ i) {
ask[i] = transfer[i];
ask[i].pos = N - transfer[i].pos + 1;
ask[i].val = N - ask[i].val + 1;
}
CDQ(1, M);
for (int i = 1; i <= M; ++ i) {
sum -= answer[i - 1];
std::cout << sum << '\n';
}
return 0;
}