2019ICPC银川网络赛 - L. Continuous Intervals

题意:

给定一个长度为 n 的数组,求满足 mx - mn + 1 = cnt 的区间个数,其中 mx、mn 为区间最值,cnt 为区间数字种类。 (n <= 1e5)

链接:

https://cn.vjudge.net/problem/Gym-102222L

题解:

移项得到 mx - mn - cnt = -1,令 T[L, R] = mx - mn - cnt,可每次固定区间右端点R,求满足 T[L, R] = -1 (1 <= L <= R) 的左端点L个数,注意到 T[L, R] >= -1 且 T[R, R] = -1,考虑在每个R时,用线段树维护 T[L, R] (1 <= L <= R) 最小值,贡献为最小值个数。

接下来考虑 R - 1 -> R 时的维护,对于 cnt 只需找到 A[R] 上一出现位置last,线段树区间 [last + 1, R] -1 即可;对于 mx、mn 的维护,只需找到 A[R] 作为极值的左端点L,相应进行区间加减操作,可用单调栈维护。

参考代码:
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int maxm = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

map<int, int> mp;
int mn[maxn << 2], num[maxn << 2], add[maxn << 2];
pii smx[maxn], smn[maxn];
int a[maxn], n, p1, p2;

void build(int l, int r, int rt){

	mn[rt] = add[rt] = 0;
	num[rt] = r - l + 1;
	if(l == r) return;
	int mid = gmid;
	build(l, mid, lson);
	build(mid + 1, r, rson);
}

void pushUp(int rt){

	mn[rt] = min(mn[lson], mn[rson]);
	if(mn[lson] == mn[rson]) num[rt] = num[lson] + num[rson];
	else if(mn[lson] < mn[rson]) num[rt] = num[lson];
	else num[rt] = num[rson];
}

void pushDown(int rt){

	if(add[rt]){

		add[lson] += add[rt], add[rson] += add[rt];
		mn[lson] += add[rt], mn[rson] += add[rt];
		add[rt] = 0;
	}
}

void update(int l, int r, int rt, int L, int R, int val){

	if(l >= L && r <= R){

		mn[rt] += val, add[rt] += val;
		return;
	}
	int mid = gmid;
	pushDown(rt);
	if(L <= mid) update(l, mid, lson, L, R, val);
	if(R > mid) update(mid + 1, r, rson, L, R, val);
	pushUp(rt);
}

int main(){

//    ios::sync_with_stdio(0); cin.tie(0);
    int t, cas = 0; scanf("%d", &t);
    while(t--){

        scanf("%d", &n);
        mp.clear(); p1 = p2 = 0;
        smx[p1] = {inf, 0}, smn[p2] = {0, 0};
        for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
        build(1, n, 1); ll ret = 0;
        for(int i = 1; i <= n; ++i){

			int las = mp[a[i]];
			update(1, n, 1, las + 1, i, -1);
			mp[a[i]] = i;
			while(a[i] >= smx[p1].first){

				pii c = smx[p1--], b = smx[p1];
				if(a[i] > c.first) update(1, n, 1, b.second + 1, c.second, a[i] - c.first);
			}
			smx[++p1] = {a[i], i};
			while(a[i] <= smn[p2].first){

				pii c = smn[p2--], b = smn[p2];
				if(a[i] < c.first) update(1, n, 1, b.second + 1, c.second, c.first - a[i]);
			}
			smn[++p2] = {a[i], i};
			ret += num[1];
        }
        printf("Case #%d: %lld\n", ++cas, ret);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值