BZOJ 5011: [Jx2017]颜色

Description

可怜有一个长度为n的正整数序列Ai,其中相同的正整数代表着相同的颜色。
现在可怜觉得这个序列太长了,于是她决定选择一些颜色把这些颜色的所有位置都删去。
删除颜色i可以定义为把所有满足Aj=i的位置j都从序列中删去。
然而有些时候删去之后,整个序列变成了好几段,可怜不喜欢这样,于是她想要知道有多
少种删去颜色的方案使得最后剩下来的序列非空且连续。
例如颜色序列{1,2,3,4,5},删除颜色3后序列变成了{1,2}和{4,5}两段,不满足条件。
而删除颜色1后序列变成了{2,3,4,5},满足条件。
两个方案不同当且仅当至少存在一个颜色i只在其中一个方案中被删去。

Input

第一行输入一个整数T表示数据组数。
每组数据第一行输入一个整数n表示数列长度。
第二行输入n个整数描述颜色序列。
1 ≤ T, ∑ n ≤ 3 × 10^5, 1 ≤ Ai ≤ n

Output

对于每组数据输出一个整数表示答案

Sample Input

1

5

1 3 2 4 3

Sample Output

6

//满足条件的删颜色方案有 {1}, {1, 3}, {1, 2, 3}, {1, 3, 4}, {2, 3, 4}, ∅

HINT

设每种颜色出现位置的最小值和最大值为mn[i]和mx[i]。
那么题目其实就是问有多少个区间[l,r]满足max(mx[l..r])<=r且min(mn[l,r])>=l。
这个可以枚举右端点,然后用一个单调栈来维护mx,用另外一个单调栈来维护所有满足mn限制的左端点,每次只要在单调栈上二分一下即可。

CODE

#include <bits/stdc++.h>

const int N = 300005;

int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

int l[N],r[N];
int mx[N],mn[N];
int c[N];

int s1[N],s2[N];

int binary(int tot, int tmp)
{
    int L = 1, R = tot;
    while (L <= R)
    {
        int mid = (L + R) >> 1;
        if (r[s1[mid]] > tmp)
            L = mid + 1;
        else R = mid - 1;
    }
    return s1[L - 1] + 1;
}

int main()
{
    int T = read();
    while (T--)
    {
        int n = read();
        for (int i = 1; i <= n; i++)
            mn[i] = n, mx[i] = 1;
        for (int i = 1; i <= n; i++)
            c[i] = read(), mn[c[i]] = std::min(mn[c[i]], i), mx[c[i]] = std::max(mx[c[i]], i);
        for (int i = 1; i <= n; i++)
            l[i] = mn[c[i]], r[i] = mx[c[i]];
        long long ans = 0;
        int t1 = 0, t2 = 0;
        for (int i = 1; i <= n; i++)
        {
            while (t1 && r[s1[t1]] <= r[i])
                t1--;
            s1[++t1] = i;s2[++t2] = i;
            while (t2 && s2[t2] > l[i])
                t2--;
            int lim = binary(t1, i), r = std::lower_bound(s2 + 1, s2 + t2 + 1, lim) - s2;
            ans += t2 - r + 1;
        }
        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值