2019杭电多校第二场1012[线段树求区间覆盖]

1012 Longest Subarray
Longest Subarray
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 873 Accepted Submission(s): 265

Problem Description
You are given two integers C,K and an array of N integers a1,a2,…,aN. It is guaranteed that the value of ai is between 1 to C.

We define that a continuous subsequence al,al+1,…,ar(l≤r) of array a is a good subarray if and only if the following condition is met:

∀x∈[1,C],∑i=lr[ai=x]=0or∑i=lr[ai=x]≥K

It implies that if a number appears in the subarray, it will appear no less than K times.

You should find the longest good subarray and output its length. Or you should print 0 if you cannot find any.

Input
There are multiple test cases.

Each case starts with a line containing three positive integers N,C,K(N,C,K≤105).

The second line contains N integer a1,a2,…,aN(1≤ai≤C).

We guarantee that the sum of Ns, the sum of Cs and the sum of Ks in all test cases are all no larger than 5×105.

Output
For each test case, output one line containing an integer denoting the length of the longest good subarray.

Sample Input
7 4 2
2 1 4 1 4 3 2

Sample Output
4

Source
2019 Multi-University Training Contest 2

题意:给一个序列,求一个最长的子串满足:每种出现的数字的个数>=k

题解:固定右端点,那么左端点合法的范围应该是一个区间[1,x],其中x是在右端点左侧并且距离右端点第k近的和右端点值一样的数字的位置,同时固定右端点之后,不取右端点值的右侧合法的范围应该是在[y,z],其中y是右端点右侧并且距离右端点最近的和右端点值一样的数字的位置,问题即转化为区间覆盖问题,固定右端点同时更新区间覆盖,通过寻找被覆盖次数=c的最小下标i作为左端点完成对答案的更新

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" is "<<x<<endl;
const int maxn=3e5+5;
vector<int>pos[maxn];
int now[maxn],a[maxn];
int n,c,k;
struct node{
    int l;
    int r;
    int maxx;
    int lazy;
}nod[maxn<<2];
void pushup(int rt){
    nod[rt].maxx=max(nod[rt<<1].maxx,nod[(rt<<1)|1].maxx);
}
void build(int l,int r,int rt){
    nod[rt].l=l;
    nod[rt].r=r;
    nod[rt].maxx=0;
    nod[rt].lazy=0;
    if(l==r){
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,(rt<<1)|1);
    pushup(rt);
}
void pushdown(int rt){
    if(nod[rt].lazy){
        nod[rt<<1].maxx+=nod[rt].lazy;
        nod[(rt<<1)|1].maxx+=nod[rt].lazy;
        nod[rt<<1].lazy+=nod[rt].lazy;
        nod[(rt<<1)|1].lazy+=nod[rt].lazy;
        nod[rt].lazy=0;
    }
}
void update(int l,int r,int L,int R,int rt,int val){
    if(L>R)return;
    if(l>=L&&r<=R){
        nod[rt].maxx+=val;
        nod[rt].lazy+=val;
        return;
    }
    int mid=(l+r)>>1;
    pushdown(rt);
    if(mid>=L)update(l,mid,L,R,rt<<1,val);
    if(mid<R)update(mid+1,r,L,R,(rt<<1)|1,val);
    pushup(rt);
}
int query(int l,int r,int rt){
    if(l==r){
        if(nod[rt].maxx==c)
            return l;
        else
            return -1;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(nod[rt<<1].maxx==c){
        return query(l,mid,rt<<1);
    }
    else if(nod[(rt<<1)|1].maxx==c){
        return query(mid+1,r,(rt<<1)|1);
    }
    return -1;
}
int main(){
    while(scanf("%d%d%d",&n,&c,&k)==3){
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        build(1,n,1);
        for(int i=1;i<=c;i++){
            pos[i].clear();
            pos[i].push_back(0);
            now[i]=0;
        }
        for(int i=1;i<=n;i++){
            pos[a[i]].push_back(i);
        }
        for(int i=1;i<=c;i++){
            pos[i].push_back(n+1);
            update(1,n,pos[i][0]+1,pos[i][1]-1,1,1);
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            int xx=a[i];
            update(1,n,pos[xx][now[xx]]+1,pos[xx][now[xx]+1]-1,1,-1);
            if(now[xx]>=k)update(1,n,1,pos[xx][now[xx]-k+1],1,-1);
            now[xx]++;
            update(1,n,pos[xx][now[xx]]+1,pos[xx][now[xx]+1]-1,1,1);
            if(now[xx]>=k)update(1,n,1,pos[xx][now[xx]-k+1],1,1);
            int ac=query(1,n,1);
            if(ac==-1)continue;
            ans=max(ans,i-ac+1);
        }
        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值