ACM-ICPC 2018 徐州赛区网络预赛 G. Trace
标签
- 线段树
- 逆向
- 离散化
简明题意
- 有n个左下端点在原点的矩形,依次给出他们右上端点。对于给出的每个矩形,需要画出他们的轮廓,然而每个矩形会覆盖之前矩形的轮廓。问最后轮廓线有多长?
思路
- 首先横线和竖线是可以分开考虑的,所以我们分开考虑,我们只考虑横线
- 注意到每条线在过程中会被后续的线影响,这样很复杂,很难知道一条线的状态。但最终每条线被覆盖了多少是确定的,这样的话,我们用贡献法,直接讨论每条线最终的贡献。
- 我们先思考,一条横线,什么时候会被覆盖。答案是,一条横线只会被在他后面的,且比他高的横线覆盖一部分。那么对于第一条横线,我们不能计算他对答案的贡献,因为他会被他后面的横线影响,只有当他后面的横线都确定了,才能确定这条直线被覆盖的部分。这就启示我们,从后往前考虑每条线。
- 回忆刚刚的话,一条横线只会被在他后面的,且比他高的横线覆盖一部分,那么我们从后往前考虑到x[i]时,他会被所有比他高的横覆盖一部分,但最终被覆盖的长度,是比他高的横线中横坐标最长的那个,这是,我们就对y建立线段树,去维护区间[y1, y2]中,最大的x。当然y太大了,但是我们只需要知道y的相对位置,离散化一下就可以了。
注意事项
不开longlong见祖宗
总结
- 贡献法
- 逆向思考
AC代码
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 5e4 + 10;
int n, x[maxn], y[maxn];
vector<int> lsx, lsy;
struct Seg_tree
{
struct Node
{
int l, r, max;
};
Node tree[maxn * 4];
void build(int o, int l, int r)
{
tree[o].l = l, tree[o].r = r;
if (l == r)
return;
int mid = (l + r) / 2;
build(o * 2, l, mid);
build(o * 2 + 1, mid + 1, r);
}
int ask(int o, int l, int r)
{
if (tree[o].l == l && tree[o].r == r)
return tree[o].max;
int mid = (tree[o].l + tree[o].r) / 2;
if (r <= mid)
return ask(o * 2, l, r);
else if (l > mid)
return ask(o * 2 + 1, l, r);
else
return max(ask(o * 2, l, mid), ask(o * 2 + 1, mid + 1, r));
// return max(tree[o * 2].max, tree[o * 2 + 1].max);
}
void change(int o, int x, int c)
{
if (tree[o].l == tree[o].r)
{
tree[o].max = max(tree[o].max, c);
return;
}
int mid = (tree[o].l + tree[o].r) / 2;
if (x <= mid)
change(o * 2, x, c);
else
change(o * 2 + 1, x, c);
tree[o].max = max(tree[o * 2].max, tree[o * 2 + 1].max);
}
};
Seg_tree tx, ty;
int new_x[maxn], new_y[maxn];
void solve()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d%d", &x[i], &y[i]), lsx.push_back(x[i]), lsy.push_back(y[i]);
//离散化
sort(lsx.begin(), lsx.end()), sort(lsy.begin(), lsy.end());
int lenx = unique(lsx.begin(), lsx.end()) - lsx.begin(), leny = unique(lsy.begin(), lsy.end()) - lsy.begin();
for (int i = 1; i <= n; i++)
new_x[i] = lower_bound(lsx.begin(), lsx.end(), x[i]) - lsx.begin() + 1, new_y[i] = lower_bound(lsy.begin(), lsy.end(), y[i]) - lsy.begin() + 1;
tx.build(1, 1, leny), ty.build(1, 1, lenx);
long long ansx = 0, ansy = 0;
//开始倒序枚举
for (int i = n; i >= 1; i--)
{
//处理横线
ansx += x[i] - tx.ask(1, new_y[i], leny);
tx.change(1, new_y[i], x[i]);
//处理竖线
ansy += y[i] - ty.ask(1, new_x[i], lenx);
ty.change(1, new_x[i], y[i]);
}
printf("%lld", ansx + ansy);
}
int main()
{
freopen("Testin.txt", "r", stdin);
solve();
return 0;
}
双倍经验
- 无