【2019银川网络赛】L:Continuous Intervals

题目大意:给定一个长度为 N 的序列,定义连续区间 [l, r] 为:序列的一段子区间,满足 [l, r] 中的元素从小到大排序后,任意相邻两项的差值不超过1。求一共有多少个连续区间。

题解:单调栈 + 线段树
首先,对于区间计数类问题常规的思路是枚举区间的左端点或右端点,统计以该点为端点的区间个数,加入答案贡献。
对于这道题来说,不妨枚举答案的右端点 r,那么对于每个 r,需要快速得出有多少个左端点 l,使得区间 [l, r] 满足连续区间的性质。若能在 \(O(logn)\) 的时间内得出答案即可解决本题。
根据连续区间的性质,可知连续区间的定义等价于
\[
max(a[l...r])-min(a[l...r])+1 \ge cnt
\]
其中,cnt 为区间 [l, r] 中不同数字的个数。可以发现,只有取得等号的时候才满足连续区间的性质,即:\(max - min - cnt = -1\)。因此,对于每个枚举到的右端点 r,我们需要知道每个小于 r 的 l, [l, r] 区间的最大值和最小值以及区间不同数的个数。
可以利用线段树维护 \(max - min - cnt\),只需维护区间最小值以及区间最小值的个数,即可在线段树上快速回答询问。
维护区间最值可以利用单调栈,即:第 i 个元素入栈时,栈内元素由于单调性,自然维护了区间[i, r] 的最值,每次从栈中弹出元素时,需要在线段树上修改维护的最值贡献。
维护区间颜色数是一个经典问题,即:维护一个 pre 数组,用于记录上一次某个元素出现的位置。

代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

struct node { // max - min - cnt >= -1
    #define ls(o) t[o].lc
    #define rs(o) t[o].rc
    int lc, rc;
    LL mi, cnt, add;
};
vector<node> t;
int tot, rt;
inline void up(int o) {
    if (t[ls(o)].mi == t[rs(o)].mi) {
        t[o].mi = t[ls(o)].mi;
        t[o].cnt = t[ls(o)].cnt + t[rs(o)].cnt;
    } else if (t[ls(o)].mi < t[rs(o)].mi) {
        t[o].mi = t[ls(o)].mi;
        t[o].cnt = t[ls(o)].cnt;
    } else {
        t[o].mi = t[rs(o)].mi;
        t[o].cnt = t[rs(o)].cnt;
    }
}
inline void down(int o) {
    if (t[o].add != 0) {
        t[ls(o)].mi += t[o].add, t[ls(o)].add += t[o].add;
        t[rs(o)].mi += t[o].add, t[rs(o)].add += t[o].add;
        t[o].add = 0;
    }
}
inline int newnode() {
    ++tot;
    t[tot].lc = t[tot].lc = t[tot].mi = t[tot].cnt = t[tot].add = 0;
    return tot;
}
void build(int &o, int l, int r) {
    o = newnode();
    if (l == r) {
        t[o].mi = t[o].add = 0, t[o].cnt = 1;
        return;
    }
    int mid = l + r >> 1;
    build(ls(o), l, mid);
    build(rs(o), mid + 1, r);
    up(o);
}
void modify(int o, int l, int r, int x, int y, LL add) {
    if (l == x && r == y) {
        t[o].mi += add, t[o].add += add;
        return;
    }
    int mid = l + r >> 1;
    down(o);
    if (y <= mid) {
        modify(ls(o), l, mid, x, y, add);
    } else if (x > mid) {
        modify(rs(o), mid + 1, r, x, y, add);
    } else {
        modify(ls(o), l, mid, x, mid, add);
        modify(rs(o), mid + 1, r, mid + 1, y, add);
    }
    up(o);
}

int main() {
    int T, kase = 0;
    scanf("%d", &T);
    while (T--) {
        int n;
        scanf("%d", &n);
        vector<int> a(n + 1);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        t.resize(2 * n), tot = 0;
        build(rt, 1, n);
        vector<pair<int, int>> mi(n + 1), mx(n + 1);
        int top1 = 0, top2 = 0;
        map<int, int> pre;
        LL ans = 0;
        for (int i = 1, now; i <= n; i++) { // <val, pos>
            now = i;
            while (top1 > 0 && a[i] < mi[top1].first) {
                int pos = mi[top1 - 1].second;
                modify(rt, 1, n, pos + 1, now - 1, mi[top1].first - a[i]);
                --top1;
                now = pos + 1;
            }
            mi[++top1] = make_pair(a[i], i);
            now = i;
            while (top2 > 0 && a[i] > mx[top2].first) {
                int pos = mx[top2 - 1].second;
                modify(rt, 1, n, pos + 1, now - 1, a[i] - mx[top2].first);
                --top2;
                now = pos + 1;
            }
            mx[++top2] = make_pair(a[i], i);
            if (pre.find(a[i]) != pre.end()) {
                int pos = pre[a[i]];
                modify(rt, 1, n, pos + 1, i, -1);
            } else {
                modify(rt, 1, n, 1, i, -1);
            }
            pre[a[i]] = i;
            if (t[rt].mi == -1) {
                ans += t[rt].cnt;
            }
        }
        printf("Case #%d: %lld\n", ++kase, ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/wzj-xhjbk/p/11453490.html

时间: 2024-08-29 21:04:25

【2019银川网络赛】L:Continuous Intervals的相关文章

2019银川网络赛

??A. Maximum Element In A Stack ??B. Rolling The Polygon ??C. Caesar Cipher ??D. Take Your Seat E. 2-3-4 Tree ??F. Moving On G. Factories 树形DP + 背包 https://blog.csdn.net/dllpXFire/article/details/81085093 https://blog.csdn.net/starlet_kiss/article/de

2019徐州网络赛 XKC&#39;s basketball team 线段树

网址:https://nanti.jisuanke.com/t/41387 题意: 大家好,我是训练时长两年半的个人练习生蔡徐坤,我的爱好是唱,跳,rap,篮球. 给出一段长度为$n,(n \leq 1e5)$的序列,对每一个数,求出它和它后面比它大$m$的数中间夹着的数的数量,没有输出$-1$. 题解: 直接建线段树,维护最大值,然后查询时对第$i$个数,搜索区间$[i,n]$之中大于$num[i]+m$的值的位置的最大值,具体操作是先限定区间,然后求出所有合法位置,取最大值,如果搜索不到则返

ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval

ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval 题目大意:给一个长度为n,值域为[1, n]的序列{a},要求支持m次操作: 单点修改 1 pos val 询问子区间中某个值域的数的个数,连续的相同数字只记为一个.(即统计数字段的个数) 2 L R x y 数据范围: 1 ≤ n,m ≤ 2×10^5 1 ≤ a[i] ≤ n 解题思路: 连续重复的数字只记一次.所以考虑将每个数字段除第一个出现外的数字都删去(记为0).在读入操作的时候暴力模拟,同时维护

2019 CCPC网络赛

一到网络赛,大家都是东亚之光 1001 00:23:46 solved by hl 签到 枚举一下位就行了 #include <map> #include <set> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <vector> #include <string> #include <bitset

19南昌网络赛L

校赛打杂没施展开. 题意:一开始给你一颗 (0,0)到(0,l)的树. 这棵树每一年会长出来三个幼芽(雾),长度均为l/4,方向分别是左转60,右转60,和不变. 年份<=14 考虑3^14直接暴力存边然后考虑每条边贡献.发现奇难无比. 考虑剪枝. 注意到如果一根树枝的被砍掉了那么他所有的孩子全都不算贡献了.妙啊!变成傻逼题. 抄一下板子. 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef double db; 4 c

query 2019徐州网络赛(树状数组)

query \[ Time Limit: 2000 ms \quad Memory Limit: 262144 kB \] 题意 补题才发现比赛的时候读了一个假题意.... 给出长度为 \(n\) 的排列,在给出 \(m\) 次询问,每次询问有一对 \(l.r\),问满足 \(min(ai, aj) = gcd(ai, aj)\) 的 \(pair(i, j)\) 对数. 思路 考虑离线做 先把每个数出现的位置记录下来,然后预处理出所有的 \(pair\). 对于一个数字 \(x\),其满足条件

2019 南昌网络赛icpc I题 cdq分治或分块

题意:给你一个数组,然后每次有两种操作,操作一是修改数组里的数,操作二是查询区间[ l , r ] 里有多少个子区间满足以下条件:1.子区间内的数全部相同.2.子区间内的数在x到y之间.3.子区间得是不能延伸的. 题目链接:https://nanti.jisuanke.com/t/41356 题解:首先转化问题,设 b[ i ] = a[i]==a[i-1] ? 0 : a[i],然后问题就变成了问询区间内有多少个x到y之间的数.(注意左端点特判)这不就是主席树....带修改...好,树状数组加

NOJ 网络赛 L题 送花

题目: 送花 时间限制(普通/Java) : 1000 MS/ 3000 MS          运行内存限制 : 65536 KByte总提交 : 116            测试通过 : 41 题目描述 萌妹纸一般都比较喜欢漂亮的鲜花.每逢各种节日,她们都想收到鲜花作为礼物.如果你是有妹纸滴人,经常不送妹纸花的话,结果可想而知了. 当然咯,妹纸都是通情达理的,不会因为某几次你木有送花,就发你好人卡了.王童鞋作为一个比较节俭(抠门)的人便知道这一道理,因此他想在妹纸不给他发好人卡的前提下,送

2018icpc南京网络赛-L Magical Girl Haze (分层图最短路)

题意: 有向图,可以把k条路的长度变为0,求1到n的最短路 思路: 将图复制k份,一共k+1层图,对于每一条i→j,都连一条低层的i→高层的j,并且权值为0 即对每一对<i,j,w>,都加边<i,j,w>,<i+n,j+n,w>,<i+2n,j+2n,w>,....,<i+kn,j+kn,w> 同时加“楼梯”<i,j+n,0>,<i+n,j+2n,0>,...,<i+(k-1)n, j+kn> 然后跑一个1~(