題名
与えられたMMM市、毎年1市が選出され、旧NNN年間に都市で開催された大会の状況。翌年は、大会開催数が最も少ない都市で年次大会を開催しますが、開催回数が最も少ない都市が多い場合は、開催回数が最も少ない都市で開催します。今QQを与えるQ質問、KKに聞くたびにK年に大会はどの都市で開催されますか?
分析
個人的な習慣のため、タイトルにnnを入れてくださいnとmmmは交換を意味します。
問題をnnに抽象化できます幅が同じで高さが異なるn個の長方形(初期の高さを指定)で、番号が最も小さい最短の長方形が選択され、その高さが+1になるたびに、この操作が継続的に繰り返され、答えはKKになります。K回選択された長方形の数。
最小の数を選択する必要があることがわかったので、最初にソートすることを躊躇しません。(トラブルの場合は最初にシーケンスしてください)
したがって、元の問題を次の図に変えることができます。
同じ高さのいくつかの長方形が「プラットフォーム」を形成することがわかりました。したがって、この「プラットフォーム」を見つける方法を考えてください。
「プラットフォーム」のセクションを別々に取り出すと、高さが同じであるため、それらを選択する順序を番号で並べ替えて円を形成する必要があります。したがって、このプラットフォームに基づいてkkを要求したい場合k個の選択肢がある場合、実際に探しているのは、プラットフォームのこのセグメントのk番目の数値です%lenk \%lenk %l e nが小さい(ここでlen lenl e nはプラットフォームの長さであり、k%len = 0 k \%len = 0の場合k %l e n=0、次に番号が最も大きいもの)。
次に、このシークプラットフォームの番号kkkが小さい操作は、加重線セグメントツリーバランスツリーを使用して維持できます。
残念ながら、問題には明らかに複数のプラットフォームがあるため、「ピットを埋める」ことを低いものから高いものへと検討します。公式ソリューションの写真を盗みます。
処理の便宜のために、問い合わせをオフラインにしてkkに従います。kのサイズ(つまり、優先順位)がソートされます。
たとえば、この図では、最初の3つの長方形が2つのプラットフォームを形成しており、高さが高い{2、5} \ {2,5 \}を見つけます。{ 2 、5 }うに対してではない3 33が持っている影響を、あなたがするまで待たなければならない3 33リーチ{2、5} \ {2,5 \}{ 2 、5 }は高さの後に影響を受けます。その後、我々は安全に置くことができる3 33で充填{2,5} \ {2,5 \}{ 2 、5 }高さ、次に置く3,33プラットフォームに参加して、{2、3、5} \ {2,3,5 \}を形成します{ 2 、3 、5 }そのようなプラットフォーム。明らかに、ピットを埋めた後、すべてのプラットフォームは元のシーケンスのプレフィックスになります。ピットを埋める過程で、いくつかの質問に答えることができるので、答えはプラットフォームの性質に基づいています。同時に、プラットフォームに新しく追加された要素を、次のクエリのためにウェイトラインセグメントツリーに挿入する必要もあります。
実装の詳細
プラットフォームとクエリを処理するときに、現在処理されている長方形またはクエリを示すポインタをそれぞれ記録できます。同時に今を維持するn o wは、現在いくつかの選択が行われていることを意味します(初期値は指定された操作数です)。次に、長方形の場合、プラットフォームが毎回後方に検出され、プラットフォーム内の長方形番号がプロセスのウェイトラインセグメントツリーに挿入されます。次に、現在の場合、後方クエリを列挙今今n o wに加えて、次に充填されるプラットフォームに必要な操作の数がクエリよりも大きい場合、このクエリはこのピット充填で回答でき、回答は加重ラインセグメントツリーでクエリを実行することによって取得されます。いよいよ今n o wは、ピットを埋めるために必要な操作の数を合計します。
複雑さ
両方のポインタが線形にスキャンされるため、クエリ処理部分はO((n + q)logn)O((n + q)logn)です。O ((n+q )l o g n )、前の並べ替えにはO(nlogn)O(nlogn)が必要なためO (n l o g n )時間なので、全体の複雑さはO((n + q)logn)O((n + q)logn)です。O ((n+q )l o g n )。
コード
#include <bits/stdc++.h>
#define ll long long
#define MAX 500005
#define lc(x) (x<<1)
#define rc(x) (x<<1|1)
#define mid ((l+r)>>1)
using namespace std;
namespace sgt{
int s[MAX*4];
void push_up(int p){
s[p] = s[lc(p)]+s[rc(p)];
}
void update(int p, int l, int r, int u, int k){
if(l == r){
s[p] += k;
return;
}
if(mid >= u) update(lc(p), l, mid, u, k);
else update(rc(p), mid+1, r, u, k);
push_up(p);
}
int query(int p, int l, int r, int k){
if(l == r) return l;
if(s[lc(p)] >= k){
return query(lc(p), l, mid, k);
}
else return query(rc(p), mid+1, r, k-s[lc(p)]);
}
}
//以上权值线段树模板
struct ask{
//询问离线
ll k, id;
friend bool operator <(ask a, ask b){
return a.k < b.k;
}
}q[MAX];
struct city{
//矩形
ll h, id;
friend bool operator <(city a, city b){
if(a.h == b.h) return a.id < b.id;
return a.h < b.h;
}
}a[MAX];
int n, m;
ll Q, now, p, p1, p2, ans[MAX];
int main()
{
cin >> m >> n >> Q;
for(int i = 1; i <= n; ++i){
a[i].id = i;
}
ll x;
for(int i = 1; i <= m; ++i){
scanf("%lld", &x);
a[x].h++;
}
sort(a+1, a+n+1);
for(int i = 1; i <= Q; ++i){
scanf("%lld", &q[i].k);
q[i].id = i;
}
sort(q+1, q+Q+1);
//初始化:排序、离线
p1 = p2 = 1;
now = m;
while(p1 <= n){
p = p1;
while(a[p].h == a[p1].h){
//向后找平台,并插入到权值线段树中
sgt::update(1, 1, n, a[p].id, 1);
p++;
}
while(now+(a[p].h-a[p1].h)*(p-1) >= q[p2].k){
//处理在这次填坑范围内的询问
ll t = (q[p2].k-now)%(p-1);
if(!t) t = p-1;
ans[q[p2].id] = sgt::query(1, 1, n, t);
p2++;
}
now += (a[p].h-a[p1].h)*(p-1); //累加操作次数
p1 = p; //下一次平台的开始
}
for(int i = 1; i <= Q; ++i){
//可能还有一些询问没有处理,但此时所有矩形都已形成一个平台,直接回答,甚至不需要权值线段树
if(!ans[q[i].id]){
ll t = (q[i].k-now)%n;
if(!t) t = n;
ans[q[i].id] = t;
}
}
for(int i = 1; i <= Q; ++i){
printf("%lld\n", ans[i]);
}
return 0;
}