BZOJ5011 & 洛谷4065 & LOJ2275:[JXOI2017]颜色——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=5011

https://www.luogu.org/problemnew/show/P4065

https://loj.ac/problem/2275

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

套路裸,但我不会啊。

参考:https://www.luogu.org/blog/ShadowassIIXVIIIIV/solution-p4065

这个人的讲解有点长我就转述一下吧。

首先惯用的套路:枚举右端点,用线段树查找有多少个可能的左端点。

于是我们设$mn[i]-mx[i]$表示$i$颜色所在的区间范围。

显然当右端点$r>=mx[i]$的时候左端点一定不能在$mn[i]+1-mx[i]$这段区间中,于是用线段树维护之。

并且对于$mx[i]>r$的颜色$i$也是一定要被删除的,这个可以帮助我们确定左端点的下界。

我们显然要找的$l$为$mx[col[l]]>r$并且离$r$最近的点+1(因为$l$这个点本身是不可取的)。

我们还能发现随着$r$的增大$l$在单调不增,于是可以用单调栈来维护。

#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=3e5+5;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
int n,b[N],mx[N],mn[N];
int tr[N*4],lz[N*4];
stack<int>q;
inline void push(int a,int l,int r){
    if(lz[a]){
    int mid=(l+r)>>1;
    lz[a<<1]=lz[a<<1|1]=lz[a];
    tr[a<<1]=mid-l+1;tr[a<<1|1]=r-mid;
    lz[a]=0;
    }
}
void mdy(int a,int l,int r,int l1,int r1){
    if(l1>r1||r<l1||r1<l)return;
    if(l1<=l&&r<=r1){
    tr[a]=r-l+1;lz[a]=1;
    return;
    }
    push(a,l,r);
    int mid=(l+r)>>1;
    mdy(a<<1,l,mid,l1,r1);mdy(a<<1|1,mid+1,r,l1,r1);
    tr[a]=tr[a<<1]+tr[a<<1|1];
}
int qry(int a,int l,int r,int l1,int r1){
    if(r<l1||r1<l)return 0;
    if(l1<=l&&r<=r1)return tr[a];
    push(a,l,r);
    int mid=(l+r)>>1;
    return qry(a<<1,l,mid,l1,r1)+qry(a<<1|1,mid+1,r,l1,r1);
}
int main(){
    int t=read();
    while(t--){
    n=read();
    while(!q.empty())q.pop();
    for(int i=1;i<=n;i++)b[i]=read();
    for(int i=1;i<=n;i++)mx[i]=0,mn[i]=N;
    for(int i=1;i<=n*4;i++)lz[i]=tr[i]=0;
    for(int i=1;i<=n;i++)mx[b[i]]=max(mx[b[i]],i);
    for(int i=1;i<=n;i++)mn[b[i]]=min(mn[b[i]],i);
    ll sum=0;
    for(int i=1;i<=n;i++){
        if(i==mx[b[i]])
        mdy(1,1,n,mn[b[i]]+1,mx[b[i]]);
        else q.push(i);
        while(!q.empty()){
        if(mx[b[q.top()]]>i)break;
        q.pop();
        }
        int l=q.empty()?0:q.top();
        if(i!=l)sum+=i-l-qry(1,1,n,l+1,i);
    }
    printf("%lld\n",sum);
    }
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2018-06-19 10:32  luyouqi233  阅读(337)  评论(0编辑  收藏  举报