cf1208E(div1+div2)

好个差分!

题意:

  • 给定一个 n × w n\times w n×w矩形, 还给定n个数组, 每个数组的长度都小于等于w, 所以每个长度小于w的数组都可以在一行内左右滑动
  • 现求在保证所有数组都在矩形范围内滑动的情况下, 该矩形的每一列的最大值

数据范围: n ≤ 1000000 n \leq 1000000 n1000000

>> face <<

前置技能: 线段树+差分

Tutorial: 模拟了几个例子后发现, 每列的值只可能在数列的区间内取值, 可以想到要维护每个数组的区间最大, 这里用的线段树, 我第一种写法是针对每一列的每个位置都求一遍区间最大, degug的时候发现, 当 l e n ∗ 2 < w len *2 < w len2<w的时候, 中间有一段的最大值是整个数组的最大值, 所以又想到用差分维护答案, 这种情况可以区间修改, 当然也可以用另一个线段树维护, 不是这种情况的就只有暴力修改了

本题还遇到两个小小的波折, 其中一个是针对最大值与0取最大值, 还有一个就是线段树清零(build的时候已经覆盖了原来的数据不用清零, 清零会T)

#include <bits/stdc++.h>
using namespace std;
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rof(i, a, b) for (int i = (a); i > (b); --i)
#define oo 0x3f3f3f3f
#define ll long long
#define db double
#define eps 1e-8
#define bin(x) cout << bitset<10>(x) << endl;
#define what_is(x) cerr << #x << " is " << x << endl;
#define met(a, b) memset(a, b, sizeof(a))
#define all(x) x.begin(), x.end()
#define pii pair<int, int>
int nxt()
{
	int ret;
	scanf("%d", &ret);
	return ret;
}
const int maxn = 1e6 + 10;
ll ans[maxn];
int n, w;
int a[maxn];
struct seg
{
#define le o << 1
#define ri o << 1 | 1
#define lson t[le]
#define rson t[ri]
	struct node
	{
		int l, r, val;
	} t[maxn * 4];
	void update(int o)
	{
		assert(o <= maxn * 4);
		t[o].val = max(lson.val, rson.val);
	}
	void build(int o, int l, int r)
	{
		assert(o <= maxn * 4);
		t[o].l = l, t[o].r = r;
		if (l == r)
		{
			assert(scanf("%d", &t[o].val) == 1);
			return;
		}
		int mid = l + r >> 1;
		build(le, l, mid);
		build(ri, mid + 1, r);
		update(o);
	}
	void clear()
	{
		met(t, 0);
	}
	int query(int o, int l, int r)
	{
		assert(o <= maxn * 4);
		assert(l <= r);
		if (l <= t[o].l && r >= t[o].r)
			return t[o].val;
		int mid = t[o].l + t[o].r >> 1;
		if (mid >= r)
			return query(le, l, r);
		else if (mid < l)
			return query(ri, l, r);
		else
			return max(query(le, l, mid), query(ri, mid + 1, r));
	}

} seg1;
signed main()
{
	n = nxt(), w = nxt();
	_rep(i, 1, n)
	{
		int len = nxt();
		seg1.build(1, 1, len);
		if (len * 2 < w)
		{
			_rep(i, 1, len)
			{
				ll pre_max = max(seg1.query(1, 1, i), 0), suf_max = max(seg1.query(1, len - i + 1, len), 0);
				ans[i] += pre_max;
				ans[i + 1] -= pre_max;
				ans[w - i + 1] += suf_max;
				ans[w - i + 2] -= suf_max;
			}
			ll all_max = max(0, seg1.query(1, 1, len));
			ans[len + 1] += all_max;
			ans[w - len + 1] -= all_max;
		}
		else
		{
			_rep(i, 1, w)
			{
				int r = i, l = i - (w - len);
				ll val = seg1.query(1, max(l, 1), min(r, len));
				if (r > len || l < 1)val = max((ll)0, val);
				ans[i] += val;
				ans[i + 1] -= val;
			}
		}
		//seg1.clear();
	}

	_rep(i, 1, w)
	{
		ans[i] += ans[i - 1];
		cout << ans[i] << " ";
	}
	cout << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值