まず、クエリによってどのように数列に加算されるかを考えます。
が ずつ増えながら一方向に移動し、 の分だけ足すことになります。
右方向に移動することに限定すると、以下のように表現することができます。
#
##
###
####
#####
######
######
######
ここで、これを以下のように A と B に分けて考えます。
A
AA
AAA
AAAA
AAAAA
AAAAAA
BBBBBB
BBBBBB
考えやすくするために、考える区間を 、幅を 、B の高さを とします。
まず B から考えます。B は区間 に等しく だけ足しています。これは「imos法」によって管理することができます。
次に A を考えます。移動する方向を固定すると A も imos 法によって管理することができます。
具体的には、同じ配列に 回 imos 法を適用することで直角二等辺三角形を足すクエリを処理できます。
以上の議論と可算クエリしかないことから、右向きの A 、左向きの A 、B の可算クエリを別々に処理し、最後に足し合わせればよいです。
また、以下の考察により、 となることが分かるため、 整数型を用いることで AC を得ることができます。
- クエリによる の増加量は高々 である。
- クエリは 回なので、 は高々 である。
- つのクエリによって のある要素に足される回数は高々 回である。
以上を適切に実装することで、 でこの問題を解くことができました。
実装例(C++)
#include <bits/stdc++.h>
using namespace std;
int main()
{
int N, Q;
cin >> N >> Q;
vector<long long> imo1L(N + 2, 0), imo1R(N + 2, 0), imo2L(N + 2, 0), imo2R(N + 2, 0);
int p = 1;
long long x = 0;
while (Q--)
{
int t;
cin >> t;
if (p == t)
continue;
if (p < t)
{
imo2L[p]++, imo2L[t]--;
imo1L[p] += x;
x += t - p;
imo1L[t] -= x;
}
else
{
imo2R[p]++, imo2R[t]--;
imo1R[p] += x;
x += p - t;
imo1R[t] -= x;
}
p = t;
}
for (int i = 1; i <= N; i++)
imo1L[i] += imo1L[i - 1] + (imo2L[i] += imo2L[i - 1]);
for (int i = N; i >= 1; i--)
imo1R[i] += imo1R[i + 1] + (imo2R[i] += imo2R[i + 1]);
for (int i = 1; i <= N; i++)
cout << imo1L[i] + imo1R[i] << " ";
cout << endl;
}
ユーザー解説
ユーザー解説を書くユーザー解説はまだありません。