裏紙

ほぼ競プロ、たまに日記

CF 813 E - Army Creation

問題

Problem - E - Codeforces

問題概要

長さnの数列aと整数kが与えられる。次のようなクエリがq個与えられるので、それに答えよ:

  • クエリ(l,r) : 区間 [l , r] について、同じ値はk個までしか選べない時に、最大で何個まで選べるか

(問題を読んでもらうと分かるが、オンラインクエリになっている)

  •  1 \le n \le 100000
  •  1 \le a_i \le 100000
  •  1 \le q \le 100000
  •  1 \le l_i \le r_i \le n

イデア

オフラインクエリだったら、クエリを先読みしてMoを使ってmapで頻度数えて終わりだったのに...と思った。

クエリごとに、各値について先頭からk個を選ぶということを考えよう。i番目の要素が選ばれるには、そもそも l \le i \le rでないといけないし、lからスタートしてそれまでの間にa_iと同じ値がk個現れていてはいけないということになる。

ここで、新たな数列bを作る。bi番目の要素の値は、a[i]と同じ値で、k個前にあるindex(b=なければ-1)というものである。すると、i番目の要素が選ばれるということはb_i \lt lであることと、と言える。

あとは、区間 [l , r] について、b_i \lt lである要素がいくつあるかを数えれば良い。これは、mergesortの過程を保存したようなSegmentTreeなどで各ノードで二分探索をすればできたりする。

今回は(SegmentTreeの実装をサボりたかったので)平方分割した。

実装(C++)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define fi first
#define se second

const int N = 100000;
vector<int> idx[N];

const int B = 400;
const int INF = 19191919;

int b[N];
vector<int> sb[N/B];

int query(int l, int r){
    int ret = 0;

    int lid = l/B, rid = r/B;
    if(lid == rid){
        for(int i=l; i<=r; ++i) ret += (b[i]<l);
    }
    else{
        for(int i=l; i<B*(lid+1); ++i) ret += (b[i]<l);
        for(int bid=lid+1; bid<rid; ++bid){
            int add = lower_bound(all(sb[bid]), l) - sb[bid].begin();
            ret += add;
        }
        for(int i=B*rid; i<=r; ++i) ret += (b[i]<l);
    }

    return ret;
}

int main(){
    int n,k;
    scanf(" %d %d", &n, &k);
    vector<int> a(n);
    rep(i,n){
        scanf(" %d", &a[i]);
        --a[i];
        idx[a[i]].pb(i);
    }

    fill(b,b+N,INF);
    rep(i,N){
        int SZ = idx[i].size();

        rep(j,min(k,SZ)) b[idx[i][j]] = -1;
        for(int j=k; j<SZ; ++j) b[idx[i][j]] = idx[i][j-k];
    }
    rep(i,N) sb[i/B].pb(b[i]);
    rep(i,N/B) sort(all(sb[i]));

    int q;
    scanf(" %d", &q);

    int ans = 0;
    while(q--){
        int x,y;
        scanf(" %d %d", &x, &y);
        int l = (x+ans)%n, r = (y+ans)%n;
        if(l>r) swap(l,r);

        ans = query(l,r);
        printf("%d\n", ans);
    }
    return 0;
}