牛客练习赛73 D.离别(三指针+线段树)

题意:

在这里插入图片描述

解法:

考虑每个合法区间对答案的贡献.
那么需要先将询问离线.

枚举右端点r,对所有满足[l,r]中最多的数的个数为k的左端点l,执行c[l]++,
对于在右端点r上的询问ql,qr=r,答案ans=sum{
    
    c[ql,r]}.

sum{
    
    c[ql,r]}是区间和,由于c[]的区间和随着r的向右枚举,是动态变化的,因此用线段树维护.

剩下的难点就是,枚举到r的时候,如何找满足条件的所有l,
当固定r的时候,双指针维护一个最小的下标l,满足[l,r]中最多的数恰好出现k次,
但是[l+1,r]也许也是k次,因此固定r时,满足条件的区间是[l,r]左边的一段区间,如下图:

在这里插入图片描述

再开一个指针l2,维护满足[l2,r]中最多的数出现k-1次的最小下标.
那么[l,l2-1]就是对于当前r,满足条件的所有左端点了,
对c[l,l2-1]++就是对线段数进行区间加法.

code:

#include <bits/stdc++.h>
#define int long long
#define PI pair<int,int>
using namespace std;
const int maxm=3e5+5;
vector<PI>g[maxm];
int cnt2[maxm];
int cnt[maxm];
int ans[maxm];
int a[maxm];
int n,q,k;
struct Tree{
    
    
    int a[maxm<<2];
    int laz[maxm<<2];
    void pp(int node){
    
    
        a[node]=a[node*2]+a[node*2+1];
    }
    void pd(int node,int l,int r){
    
    
        if(laz[node]){
    
    
            int mid=(l+r)/2;
            a[node*2]+=laz[node]*(mid-l+1);
            a[node*2+1]+=laz[node]*(r-mid);
            laz[node*2]+=laz[node];
            laz[node*2+1]+=laz[node];
            laz[node]=0;
        }
    }
    void update(int st,int ed,int val,int l,int r,int node){
    
    
        if(st<=l&&ed>=r){
    
    
            a[node]+=val*(r-l+1);
            laz[node]+=val;
            return ;
        }
        pd(node,l,r);
        int mid=(l+r)/2;
        if(st<=mid)update(st,ed,val,l,mid,node*2);
        if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
        pp(node);
    }
    int ask(int st,int ed,int l,int r,int node){
    
    
        if(st<=l&&ed>=r){
    
    
            return a[node];
        }
        pd(node,l,r);
        int mid=(l+r)/2;
        int ans=0;
        if(st<=mid)ans+=ask(st,ed,l,mid,node*2);
        if(ed>mid)ans+=ask(st,ed,mid+1,r,node*2+1);
        return ans;
    }
}T;
signed main(){
    
    
    scanf("%lld%lld%lld",&n,&q,&k);
    for(int i=1;i<=n;i++){
    
    
        scanf("%lld",&a[i]);
    }
    for(int i=1;i<=q;i++){
    
    
        int l,r;scanf("%lld%lld",&l,&r);
        g[r].push_back({
    
    l,i});
    }
    int l=1,l2=1;
    for(int i=1;i<=n;i++){
    
    
        //
        cnt[a[i]]++;
        cnt2[a[i]]++;
        //
        while(cnt[a[i]]>k){
    
    
            cnt[a[l]]--;
            l++;
        }
        while(cnt2[a[i]]>=k){
    
    
            cnt2[a[l2]]--;
            l2++;
        }
        if(l2-1>=1&&cnt[a[l2-1]]==k){
    
    
            T.update(l,l2-1,1,1,n,1);
        }
        for(auto p:g[i]){
    
    
            int l=p.first,id=p.second;
            ans[id]=T.ask(l,i,1,n,1);
        }
    }
    for(int i=1;i<=q;i++){
    
    
        printf("%lld\n",ans[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/114025830
今日推荐