【题解】Good Subsegments

这题我调了大半天,搞得我心力交瘁,过了发篇题解祝贺一下。

传送门

题意是说,给定排列,求区间内有多少个连续子区间,满足出现的数连续。

注意这个连续的条件可以转换成 \(\max - \min = len - 1 = r - l\),即 \(\max - \min +\ l - r = 0\)。接下来我们照搬一个常见的套路:右移 \(r\),更新,然后处理右端点为 \(r\) 的询问。

这一切都围绕着 \(\max - \min+\ l - r\) 这个东西的维护。首先,区间数 \(0\) 是一个复杂度无 polylog 的做法,但我们注意到对于任意区间,\(\max - \min +\ l - r \ge 0\) 是恒满足的,所以问题变成了区间最小值个数,这个是可以线段树上搞的。

接下来考虑加进来一个新的数怎么更新。显然分成两部分:

  • \(\max - \min\),这两个本质相同。考虑维护两个单调栈,一个存储后缀最小值,一个存最大值。每次插入一个数,将弹掉的数和插入的数之间进行区间加操作。注意到根据单调栈的性质,每个数最多只会参与两次区间修改,所以区间修改次数是线性的。

简单的示意图:

  • \(l - r\),就是个平凡的区间修改。

所以我们维护了这个值,同时维护最小值及其出现次数。现在我们发现一件事——我们维护的是区间的子区间,所以我们还要维护区间历史 \(\min\) 次数和。

做这种事情有一个高明的技巧:再打一个时间标记。注意到所求的区间在没有更新的时间内都可以算作答案,所以每次下传时间 tag 时,要将距离上一次更新的所有历史都算上。然后就做完了。

注意事项

  1. 单调栈对应的区间,注意删除最后一个元素的时候能不能全部更新(我就这里调了三个小时 T_T);
  2. pushdown 的时候处理 tag 的顺序。

Code

#include <utility>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long ll;
const int max_n = 120000, max_s = 131072, max_trs = max_s*4+1, INF = 0x3f3f3f3f;

struct rg
{
	int l, r, id;
	ll ans;
}
qry[max_n];
pair<int, int> smx[max_n+1], smn[max_n+1];
int a[max_n], mn[max_trs], tag[max_trs], htm[max_trs];
ll cmn[max_trs], hcmn[max_trs];

inline int ls(int x) { return (x << 1); }
inline int rs(int x) { return (x << 1) | 1; }

void build(int l, int r, int id)
{
	cmn[id] = 1;
	mn[id] = l;
	if (l == r) return;
	
	int mid = (l + r) >> 1;
	build(l, mid, ls(id));
	build(mid + 1, r, rs(id));
}

inline void updmn(int& mn, ll &mnc, int nmn, ll ncmn)
{
	if (mn > nmn)
		mn = nmn, mnc = ncmn;
	else if (mn == nmn)
		mnc += ncmn;
}

inline void updata(int id, int vl)
{
	tag[id] += vl, mn[id] += vl;
}

inline void pushdtg(int id, int vl)
{
	htm[id] += vl;
	hcmn[id] += 1ll * vl * cmn[id];
}

inline int my_min(int a, int b) { return (a < b)? a:b; }

inline void pushup(int id)
{
	mn[id] = my_min(mn[ls(id)], mn[rs(id)]), cmn[id] = 0;
	if (mn[id] == mn[ls(id)]) cmn[id] += cmn[ls(id)];
	if (mn[id] == mn[rs(id)]) cmn[id] += cmn[rs(id)];
	hcmn[id] = hcmn[ls(id)] + hcmn[rs(id)];
}

inline void pushdown(int id)
{
	updata(ls(id), tag[id]), updata(rs(id), tag[id]);
	tag[id] = 0;
	if (mn[id] == mn[ls(id)]) pushdtg(ls(id), htm[id]);
	if (mn[id] == mn[rs(id)]) pushdtg(rs(id), htm[id]);
	htm[id] = 0;
}

void modify(int L, int R, int l, int r, int id, int vl)
{
	if (L <= l && r <= R)
	{
		updata(id, vl);
		return;
	}
	
	pushdown(id);
	int mid = (l + r) >> 1;
	
	if (L <= mid && l <= R)
		modify(L, R, l, mid, ls(id), vl);
	if (L <= r && mid < R)
		modify(L, R, mid + 1, r, rs(id), vl);
	pushup(id);
}

ll query(int L, int R, int l, int r, int id)
{
	if (L <= l && r <= R)
		return hcmn[id];
	
	pushdown(id);
	int mid = (l + r) >> 1; ll ret = 0;
	
	if (L <= mid && l <= R)
		ret += query(L, R, l, mid, ls(id));
	if (L <= r && mid < R)
		ret += query(L, R, mid + 1, r, rs(id));
	
	return ret;
}

signed main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	
	int n, q;
	
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	build(1, n, 1);
	
	cin >> q;
	for (int i = 0; i < q; i++)
		cin >> qry[i].l >> qry[i].r, qry[i].id = i, qry[i].l--, qry[i].r--;
	sort(qry, qry + q, [](const rg& lhs, const rg& rhs) { return lhs.r < rhs.r; });
	
	int mxtp = 0, mntp = 0, lst;
	for (int i = 0, qtr = 0; i < n; i++)
	{
		updata(1, -1);
		while (mxtp > 0 && smx[mxtp].first < a[i])
			modify(smx[mxtp-1].second + 1, smx[mxtp].second, 1, n, 1, a[i] - smx[mxtp].first), mxtp--;
		smx[++mxtp].first = a[i], smx[mxtp].second = i + 1;
		while (mntp > 0 && smn[mntp].first > a[i])
			modify(smn[mntp-1].second + 1, smn[mntp].second, 1, n, 1, smn[mntp].first - a[i]), mntp--;
		smn[++mntp].first = a[i], smn[mntp].second = i + 1;
		pushdtg(1, 1);
		
		while (qtr < q && qry[qtr].r <= i)
			qry[qtr].ans = query(qry[qtr].l + 1, i + 1, 1, n, 1), qtr++;
	}
	
	sort(qry, qry + q, [](const rg& lhs, const rg& rhs) { return lhs.id < rhs.id; });
	for (int i = 0; i < q; i++)
		cout << qry[i].ans << endl;
	
	return 0;
}
posted @ 2021-09-13 22:41  5ab  阅读(51)  评论(0编辑  收藏  举报