題名
ポータルP3709おじさんの文字列の質問
回答
少なくとも、区間内の要素で構成できる厳密に昇順のサブシーケンスの数を見つけます。その数は、同じ値の数にのみ関連しています。つまり、区間モードが解決されます。
Moチームアルゴリズムを使用して解きます。ウィルAI a_iをa私離散化、間隔内で数値xxを維持します数X cntx [X] cnt_x [X]c n tX[ x ]、そして数はkkですk桁の数cntk [k] cnt_k [k]c n tK[ k ]、すなわちO(1)O(1)O (1 )更新間隔で最も多く出現する桁数。合計時間計算量O(NM)O(N \ sqrt M)O (NM)。
Moチームは、左右の境界を変更するときに合理性を確保する必要があることに注意してください。したがって、間隔変更シーケンスの合理性を確保する必要があります。つまり、最初に間隔を拡大し、次に間隔を縮小します。パリティソートの最適化は、ブロックが隣接していることを確認するために、ブロックの最後の1次元番号に対応する必要があります。
実装では、論理演算子の最適化に注意を払う必要があります。式が確立されると、次のいくつかの操作は直接スキップされます。論理判定ステートメントでインクリメント、デクリメント、およびその他の操作を実行する必要がある場合は、論理に注意を払う式の最初の項。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200005;
int N, M, X[maxn], xn, xs[maxn];
int res, id[maxn], cnt_x[maxn], cnt_k[maxn], rec[maxn];
struct node
{
int l, r, k;
bool operator<(const node &b) const
{
if (id[l] != id[b.l])
return l < b.l;
return (id[l] & 1) ? r < b.r : r > b.r;
}
} Q[maxn];
inline void add(int i)
{
int &k = cnt_x[X[i]];
if (res == k)
++res;
--cnt_k[k], ++cnt_k[++k];
}
inline void del(int i)
{
int &k = cnt_x[X[i]];
--cnt_k[k];
if (!cnt_k[k] && res == k)
--res;
++cnt_k[--k];
}
int main()
{
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; ++i)
scanf("%d", X + i), xs[i] = X[i];
sort(xs + 1, xs + N + 1);
xn = unique(xs + 1, xs + N + 1) - (xs + 1);
for (int i = 1; i <= N; ++i)
X[i] = lower_bound(xs + 1, xs + xn + 1, X[i]) - xs;
int w = sqrt(M), t = ceil((double)N / w);
for (int i = 1; i <= t; ++i)
for (int l = (i - 1) * w + 1, r = min(i * w, N), j = l; j <= r; ++j)
id[j] = i;
for (int i = 1, l, r; i <= M; ++i)
scanf("%d%d", &l, &r), Q[i] = node{
l, r, i};
sort(Q + 1, Q + M + 1);
for (int i = 1, l = Q[1].l, r = l - 1, ql, qr; i <= M; ++i)
{
ql = Q[i].l, qr = Q[i].r;
while (l > ql)
add(--l);
while (r < qr)
add(++r);
while (l < ql)
del(l++);
while (r > qr)
del(r--);
rec[Q[i].k] = -res;
}
for (int i = 1; i <= M; ++i)
printf("%d\n", rec[i]);
return 0;
}