ACM-ICPC 2018 徐州赛区网络预赛 G. Trace 线段树+逆向+离散化

本文介绍了解决ACM-ICPC2018徐州赛区G题的一种方法,该题要求计算多个矩形轮廓线的总长度。通过逆向思考和贡献法,文章详细阐述了如何利用线段树和离散化技术来高效解决此问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


ACM-ICPC 2018 徐州赛区网络预赛 G. Trace


标签

  • 线段树
  • 逆向
  • 离散化

简明题意

  • 有n个左下端点在原点的矩形,依次给出他们右上端点。对于给出的每个矩形,需要画出他们的轮廓,然而每个矩形会覆盖之前矩形的轮廓。问最后轮廓线有多长?

思路

  1. 首先横线和竖线是可以分开考虑的,所以我们分开考虑,我们只考虑横线
  2. 注意到每条线在过程中会被后续的线影响,这样很复杂,很难知道一条线的状态。但最终每条线被覆盖了多少是确定的,这样的话,我们用贡献法,直接讨论每条线最终的贡献。
  3. 我们先思考,一条横线,什么时候会被覆盖。答案是,一条横线只会被在他后面的,且比他高的横线覆盖一部分。那么对于第一条横线,我们不能计算他对答案的贡献,因为他会被他后面的横线影响,只有当他后面的横线都确定了,才能确定这条直线被覆盖的部分。这就启示我们,从后往前考虑每条线。
  4. 回忆刚刚的话,一条横线只会被在他后面的,且比他高的横线覆盖一部分,那么我们从后往前考虑到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;
}

双倍经验

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值