题目链接

神仙思路。
每次g种树树把左端点插到一个树状数组\(a\),右端点插\(b\)
每次查询查\(qry(r,a)-qry(l-1,b)\)

为什么这样是对的呢?
\(qry(r,a)\)表示所有左端点在查询区间\(l\)右边的,这些树有可能会对答案产生贡献。
但是有些树没有贡献,只有这些区间右端点比\(l\)小的时候。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <bits/stdc++.h>
using namespace std;

const int N = 5e4 + 7;
int n, m, a[N], b[N];

void upd(int x, int *a) {for (; x <= n; x += x & -x) a[x] += 1;}
int qry(int x, int *a) {int res = 0; for (; x; x -= x & -x) res += a[x]; return res;}

int main() {
scanf("%d %d", &n, &m);
while (m--) {
int op, l, r;
scanf("%d %d %d", &op, &l, &r);
if (op == 1) upd(l, a), upd(r, b);
else printf("%d\n", qry(r, a) - qry(l - 1, b));
}
return 0;
}

题目链接
\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{k=1}^{n}[(j\mid i) \land ((j+k)\mid i)]\)
\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{d=j+1}^{j+n}[(j\mid i) \land (d\mid i)]\)

把枚举后验证变为直接计算贡献,显然只有当\(j,d\)\(i\)的约数时才有贡献。
\(i\)的约数中选两个大小不同的约数的选法有\(\binom{\sigma-0 i}{2}\)种。
即求\(\sum\limits_{i=1}^n\frac{\sigma-0^2 i-\sigma-0 i}{2}\)

因为\(\sigma\)是积性函数,用数论分块和\(\text{min25}\) 分别求解即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <cstdio>
#include <cmathjax>

typedef long long ll;
const int N = 1e6 + 5, MOD = 998244353;
const ll i2 = 499122177;
ll n, id1[N], id2[N], w[N], g[N], p[N], sqr;
int m, cnt;
bool vis[N];

ll add(ll a, ll b) {a %= MOD, b %= MOD; return (a + b >= MOD) ? a + b - MOD : a + b;}
ll mul(ll a, ll b) {a %= MOD, b %= MOD; return a * b % MOD;}
ll dec(ll a, ll b) {a %= MOD, b %= MOD; return ((a - b) % MOD + MOD) % MOD;}

void init(int m) {
for (int i = 2; i <= m; i++) {
if (!vis[i]) p[++cnt] = i;
for (int j = 1; j <= cnt && i * p[j] <= m; j++) {
vis[i * p[j]] = 1;
if (i % p[j] == 0) break;
}
}
}

ll solve() {
ll res = 0;
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
res = add(res, mul(r - l + 1, n / l));
}
return res;
}
ll S(ll x, int y) {
if (p[y] > x || x <= 1 ) return 0;
int k = (x <= sqr) ? id1[x] : id2[n / x];
ll res = dec(g[k], mul(4, y - 1));
for (int i = y; i <= cnt && p[i] * p[i] <= x; i++) {
ll pow1 = p[i], pow2 = p[i] * p[i];
for (int e = 1; pow2 <= x; pow1 = pow2, pow2 *= p[i], e++) {
res = add(res, add(mul(mul(e + 1, e + 1), S(x / pow1, i + 1)), mul(e + 2, e + 2)));
}
}
return res;
}

int main() {
scanf("%lld", &n);
sqr = int(sqrt(n)) + 1;
init(sqr);
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
w[++m] = n / l;
g[m] = dec(w[m], 1);
(w[m] <= sqr) ? id1[w[m]] = m : id2[r] = m;
}
for (int j = 1; j <= cnt; j++)
for (int i = 1; i <= m && p[j] * p[j] <= w[i]; i++) {
int k = (w[i] / p[j] <= sqr) ? id1[w[i] / p[j]] : id2[n / (w[i] / p[j])];
g[i] = dec(g[i], dec(g[k], j - 1));
}
for (int i = 1; i <= m; i++) g[i] = mul(g[i], 4);
printf("%lld\n", mul(dec(add(S(n, 1), 1), solve()), i2));
return 0;
}

min-25筛学习笔记 参考 好像有些地方把\(F\)\(f\)搞混了,应该能看懂吧(

\(\text{min25}\)筛是一种求积性函数前缀和的筛法。 即\(\sum\limits_{i=1}^{n}F(i)\),要求\(\sum\limits_{i=1}^{n}[i\in prime]*F(i)\)可以被快速算出。 时间复杂度 $\mathjaxcal{O}(\frac{n^{{\frac{3}{4}}}}{\log-2n})$ ,空间复杂度\(\mathjaxcal{O}(\sqrt{n})\)

min-25筛

预处理

计算\(\sum\limits_{i=1}^{cnt}F(p-i)=\sum\limits_{i=1}^{n}[i\in prime]*F(i)\) 定义\(g(n,j)=\sum\limits_{i=2}^nf(i)*[i\in prime\vee r(i)>p-j]\)\(r(i)\)表示\(i\)的最小素约数。 这里,\(f\)\(F\)不一样,\(f\)的每一个数都和\(F\)为素数时的计算方法一样,因为好算\(g\)。 形象理解\(r(i)>p-j\) 表示埃氏筛第\(j\) 次后还没筛出的数\(f(i)\)之和。 因为每个\(i\)都一定有素约数\(\leq\sqrt{i}\)。 最后\(g(n,cnt)\)即为所求。

所以\({p-j}^2>n\)时,\(g(n,j)=g(n,j-1)\),因为\(p-j\)并没有多筛出的数。 \({p-j}^2\leq n\)时,考虑\(p-j\)的贡献。 多出来的部分无非就是\(r-i=p-j\)的数,这些数显然不大于\(\lfloor\frac{n}{p-j}\rfloor\),且\(r-i>p_{j-1}\)\(g(n,j)=g(n,j-1)-f(p-j)\times (g(\frac{n}{p-j},j-1)-\sum\limits_{k=1}^{j-1}f(p-k))\) 因为\({p-j}^2\leq n\),所以\(\lfloor\frac{n}{p-j}\rfloor\geq p-j\)。 因为枚举了\(p-1,p-2,\text{...},p_{j-1}\),这些数乘上\(p-j\)\(r-i<p-j\),所以应当把多算的部分减去。

计算

我们现在知道\(\sum\limits_{i=1}^{n}F(i)\)素数部分的贡献。 设一个二元组\(s(n,j)=\sum\limits_{i=2}^nF(i)[r(i)\geq p-j]\)

可以发现\(\sum\limits_{i=1}^{n}F(i)=s(n,1)+F(1)\) 考虑计算\(s(n,j)\) , 运用之前预处理的答案,素数部分的贡献是\(g(n,cnt)-\sum\limits_{i=1}^{j-1}f(p-j)\)。 合数部分我们枚举最小的质因子\(p-k\)和幂次\(e-i\) $s(n,j)=g(n,cnt)-\sum\limits_{i=1}^{j-1}F(p-j)+\sum\limits_{k=j}^{{p-k}^2\leq n}\sum\limits_{e=1}^{p-k^{e+1}\leq n}F(p-k^e)s(\frac{n}{p-k^e},k+1)+F(p-k^{e+1})$ 边界是\(n=1\vee p-j>n\)\(s(n,j)=0\) 时间复杂度 $\mathjaxcal{O}(\frac{n^{{\frac{3}{4}}}}{\log-2n})$

例题

阅读全文 »

题目链接

我甚至不知道数大小是多少? 因为要选一半的数,所以每个数被选的概率是\(\frac{1}{2}\)
我们直接随机一个数,它大概率最后会被选。

那么现在只用计算这个数最后被选时,可能的\(\gcd\)是多少。
\(\gcd\)一定是数\(x\) 的一个约数,所以分解\(x\)后求出所有约数。
求出所有\(a-i\)\(x\)\(\gcd\),用桶\(cnt[i]\)记录每个\(\gcd\)出现的次数。
要求每一个约数的\(cnt\),只需要找到所有的\(d\mid d-i\)\(cnt-d\text{加上}cnt_{d-i}\)。 而不用每一个数都唯一分解暴力找。
因为对于当前要求的\(cnt-d,\)\(a-i\)\(x\)\(\gcd\)无非两种情况
1. \(\gcd(a-i,x)=d\),这种情况已经记录。
2. 另一种是\(g=\gcd(a-i,x)\)不等于\(d\),如果\(d\mid g\),这个\(a-i\)也要记到\(cnt-d\)贡献里面。

注意在算\(cnt\)时的顺序,更新\(cnt_{\gcd}\)以后应该从小的数更新到大的数,不然有可能会算重。因为只能从\(\gcd\)那里更新下来

最后扫一遍\(cnt\)数组,更新所有\(cnt-i\geq\left\lceil\frac{n}{2}\right\rceil\)

假设随机\(k\)次,时间复杂度\(\mathjaxcal{O}(k(n\log a+\sqrt{a}+\sigma(a)^2))\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 4;
int n, a[N], vis[N], ans, p[N], e[N], tot;
vector<int> v;
unordered-map<int, int> cnt;

int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
void div(int x) {
tot = 0;
if (x == 1) {p[++tot] = 1, e[tot] = 1; return;}
for (int i = 2; i * i <= x; i++) if (x % i == 0) {
int qaq = 0;
while (x % i == 0) x /= i, qaq++;
p[++tot] = i, e[tot] = qaq;
}
if (x != 1) p[++tot] = x, e[tot] = 1;
}
void dfs(int x, int res) {
if (x == tot + 1) {
v.push-back(res);
return;
}
int now = 1;
for (int i = 0; i <= e[x]; i++, now = now * p[x]) {
dfs(x + 1, res * now);
}
}
void solve(int id) {
v.clear();
cnt.clear();
div(a[id]);
dfs(1, 1);
stable-sort(v.begin(), v.end());
for (int i = 1; i <= n; i++) cnt[gcd(a[id], a[i])]++;
for (int i = 0; i < v.size(); i++)
for (int j = i + 1; j < v.size(); j++) {
if (v[j] % v[i] == 0) cnt[v[i]] += cnt[v[j]];
}
for (int i = 0; i < v.size(); i++) if (cnt[v[i]] >= n / 2) ans = max(ans, v[i]);
}

int main() {
srand(time(0));
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int - = 1; - <= min(n, 10); -++) {
int x = rand() % n + 1;
while (vis[x]) x = rand() % n + 1;
vis[x] = 1;
solve(x);
}
printf("%d\n", ans);
return 0;
}

题目链接

题目

\(\sum\limits_{i=0}^{k}\binom{n}{i}\bmod 2333\)
## 分析
用卢卡斯定理拆组合数。
\(\sum\limits_{i=0}^{k}\binom{n/p}{i/p}\binom{n\bmod p}{i\bmod p}\bmod 2333\)

观察一下发现等于,
\(\binom{n/p}{0}\sum\limits_{i=0}^{p-1}\binom{n\bmod p}{i}+\binom{n/p}{1}\sum\limits_{i=0}^{p-1}\binom{n\bmod p}{i}+\binom{n/p}{2}\sum\limits_{i=0}^{p-1}\binom{n\bmod p}{i}+\text{...}+\binom{n/p}{k/p}\sum\limits_{i=0}^{k\bmod p}\binom{n\bmod p}{i}\)

也就是\(\left\{\binom{n/p}{0}+\binom{n/p}{1} +\text{...}+{\binom{n/p}{n/p-1}}\right\}\times \sum\limits_{i=0}^{p-1}\binom{n\bmod p}{i}+\binom{n/p}{k/p}\sum\limits_{i=0}^{k\bmod p}\binom{n\bmod p}{i}\)

我们令\(f(n,k)\)表示\(\sum\limits_{i=0}^{k}\binom{n}{i} \bmod 2333\)
\(f(n,k)=f(n/p,n/p-1)\times f(n\bmod p,p-1)+f(n\bmod p,k\bmod p)\times \binom{n/p}{k/p}\)
显然取模的可以暴力预处理,除号用卢卡斯定理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 2345, MOD = 2333;
int T, n, k, f[N][N], c[N][N];

int lucas(int n, int m) {
if (m == 0) return 1;
return lucas(n / MOD, m / MOD) * 1ll * c[n % MOD][m % MOD] % MOD;
}
int F(int n, int k) {
if (k < 0) return 0;
if (n == 0) return 1;
if (k == 0) return 1;
if (n < MOD && k < MOD) return f[n][k];
return (F(n / MOD, k / MOD - 1) * 1ll * f[n % MOD][MOD - 1] % MOD + lucas(n / MOD, k / MOD) * 1ll * f[n % MOD][k % MOD] % MOD) % MOD;
}

main() {
scanf("%lld", &T);
c[0][0] = 1;
for (int i = 1; i < N; i++)
for (int j = 0; j <= i; j++) {
if (j == 0 || j == i) c[i][j] = 1;
else c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;
}
f[0][0] = 1;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++) {
if (j == 0) f[i][j] = c[i][j];
else f[i][j] = (f[i][j - 1] + c[i][j]) % MOD;
}
while (T--) {
scanf("%lld %lld", &n, &k);
printf("%lld\n", F(n, k));
}
return 0;
}

题目链接

田忌赛马 1. 如果最强的能赢最强的,先赢一把 2. 最弱的能赢最弱的,先赢一把
3. 否则用最弱的打最强的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <bits/stdc++.h>
using namespace std;

const int N = 100005;
int n, a[N], b[N];

int solve(int *a, int *b) {
int l1 = 1, r1 = n, l2 = 1, r2 = n, res = 0;
while (l1 <= r1 && l2 <= r2) {
if (a[l1] > b[l2]) res += 2, l1++, l2++;
else if (a[r1] > b[r2]) res += 2, r1--, r2--;
else res += (a[l1] == b[r2]), l1++, r2--;
}
return res;
}

int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
sort(a + 1, a + 1 + n), sort(b + 1, b + 1 + n);
printf("%d %d", solve(a, b), n + n - solve(b, a));
return 0;
}

题目链接

把每个数唯一分解\(n=\prod p-i^{e-i}\),显然最终答案的每个质因数指数都不小于\([1,n]\)的每个数分解的指数。
但是这样是\(\mathjaxcal{O}(n\sqrt{n})\)的。

我们只用关心每个质数最大的\(e-i\)是多少。
显然是其他数都是\(1\)的时候,不断用\(n\)除就能得到\(e-i\)的值。

话说我怎么要用\(bitset\)才能过这道题啊...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e8 + 4, MOD = 100000007;
int P[6200000], tot, n;
bitset<N> vis;

ll qaq = 1;

void init(int n) {
for (int i = 2; i <= n; i++) {
if (!vis[i]) P[++tot] = i;
for (int j = 1; j <= tot && P[j] * i <= n; j++) {
vis[i * P[j]] = 1;
if (i % P[j] == 0) break;
}
}
}

int main() {
scanf("%d", &n);
init(n);
for (int i = 1; i <= tot; i++) {
int d = n, p = P[i];
while (d / p) d /= p, qaq = qaq * 1ll * p % MOD;
}
printf("%lld\n", qaq);
return 0;
}

翻译好像没太说清楚啊?QAQ
## 题意
两个人分别在看两支球队比赛, 并且只记录了自己观看一方的胜负和得分.
现在问你,是否能够从两个人的记录中选出一些合法的比赛,使得分之和最大.
合法的意思是赢了的一方得分比对方高.
注意没选的比赛也要合法.(这就是为什么第二个样例没有选\((2,3)\)的原因)

题解

并没有要求打印方案,所以有一个很好想的\(DP\)?
\(f[i][j]\)表示考虑了第一个人的前\(i\)个记录,第二个人的前\(j\)个记录时的最优答案.
转移的时候先令\(f[i][j]=max(f[i-1][j],f[i][j-1])\).
然后如果\((i,j)\)合法,\(f[i][j]=max(f[i][j],f[i-1][j-1]+a[i]+b[j]\).
其实就是\(LCS\)模型吧.

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<bits/stdc++.h>
using namespace std;

const int N = 1005;
int n, a[N], b[N];
char S[N], T[N];

int f[N][N];

int dfs(int i, int j) {
if(i < 0 || j < 0) return 0;
int& res = f[i][j];
if (~res) return res;
if(S[i] != T[j] && ((S[i] == 'W' && a[i] > b[j]) || (T[j] == 'W' && b[j] > a[i]))) res = dfs(i - 1, j - 1) + a[i] + b[j];
return res = max(res, max(dfs(i - 1, j), dfs(i, j - 1)));
}

int main() {
memset(f, -1, sizeof f);
scanf("%d", &n);
scanf("%s", S);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
scanf("%s", T);
for (int i = 0; i < n; i++) scanf("%d", &b[i]);
printf("%d\n", dfs(n - 1, n - 1));
return 0;
}

更精彩内容看这个巨佬的博文

没想到字符串匹配还能这么玩
把比较两个字符串的过程形式化, 判断两个字符串相等\(a[i]-b[i]=0\)
普通的模式串匹配
若模式串与另一个串以\(x\)结尾的字串匹配, 即\(P(x)=\sum\limits_{i=0}^{m-1}(a[i]-b[x-m+i+1])=0\).
但是这样是有问题的,两个字符串只要字符种类和个数相同就被认为匹配了...
原因是会出现负号...
\(P(x)=\sum\limits_{i=0}^{m-1}(a[i]-b[x-m+i+1])^2\)就解决这个问题了.
大力展开式子并翻转 a 串, 可以发现第三项是卷积形式, 可以用 FFT 优化

那么通配符怎么办? 重新设计\(P(x)\), 我们只需要让通配符的匹配函数都等于\(0\)就可以了.
把 * 对应\(0\), 就是满足\(P(x)=\sum\limits_{i=0}^{m-1}(a[i]-b[x-m+i+1])^2a[i]b[x-m+i+1]=0\).
大力展开式子, \(P(x)=\sum\limits_{i=0}^{m-1}a[i]b[x-m+i+1]^3+a[i]^3b[x-m+i+1]+2a[i]^2b[x-m+i+1]^2\)
翻转一下 a 串, 即\(a[i]=a'[m-i-1]\).
\(P(x)=\sum\limits_{i=0}^{m-1}a'[m-i-1]b[x-m+i+1]^3+a'[m-i-1]^3b[x-m+i+1]+2a'[m-i-1]^2b[x-m+i+1]^2\)
可以发现这三个都是卷积形式, 所以做三次 FFT, 一次 IFFT就可以解决问题了.
时间复杂度\(\mathjaxcal{O}(n\log n)\), 常数有点大, 还有这道题卡精度, 把 eps 设置大一点可过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <bits/stdc++.h>
using namespace std;

#define y1 gmdllll
typedef complex<double> cp;
const int N = 1200005;
const double pi = acos(-1.0), eps = 0.05;
int n, m, rev[N];
double x1[N], y1[N];
cp a[N], b[N], p[N];
char x[N], y[N];

void fft(cp *a, int n, int inv) {
for (int i = 1; i < n; i++) if (i < rev[i]) swap(a[i], a[rev[i]]);
for (int k = 1; k < n; k <<= 1) {
cp wn(cos(pi / k), inv * sin(pi / k));
for (int i = 0; i < n; i += 2 * k) {
cp w(1, 0.0);
for (int j = 0; j < k; j++, w *= wn) {
cp x = a[i + j], y = w * a[i + j + k];
a[i + j] = x + y, a[i + j + k] = x - y;
}
}
}
if (inv < 0.0) for (int i = 0; i < n; i++) a[i] /= n;
}

int main() {
scanf("%d %d", &m, &n);
scanf("%s %s", x , y);
reverse(x, x + m);
for (int i = 0; i < m; i++) x1[i] = (x[i] == '*') ? 0.0 : (x[i] - 'a' + 1);
for (int i = 0; i < n; i++) y1[i] = (y[i] == '*') ? 0.0 : (y[i] - 'a' + 1);
int l = 0, len = 1;
while (len <= n + m) len <<= 1, l++;
for (int i = 1; i < len; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));

for (int i = 0; i < len; i++) a[i] = cp(x1[i], 0.0), b[i] = cp((y1[i]) * (y1[i]) * (y1[i]), 0.0);
fft(a, len, 1), fft(b, len, 1);
for (int i = 0; i < len; i++) p[i] += a[i] * b[i];

for (int i = 0; i < len; i++) a[i] = cp((x1[i]) * (x1[i]) * (x1[i]), 0.0), b[i] = cp(y1[i], 0.0);
fft(a, len, 1), fft(b, len, 1);
for (int i = 0; i < len; i++) p[i] += a[i] * b[i];

for (int i = 0; i < len; i++) a[i] = cp((x1[i]) * (x1[i]), 0.0), b[i] = cp((y1[i]) * (y1[i]), 0.0);
fft(a, len, 1), fft(b, len, 1);
for (int i = 0; i < len; i++) p[i] -= cp(2.0, 0.0) * a[i] * b[i];
fft(p, len, -1);

int v[N], top = 0;
for (int i = m - 1; i < n; i++) if (fabs(p[i].real()) < eps) v[++top] = i - m + 2;
printf("%d\n", top);
for (int i = 1; i <= top; i++) printf("%d ", v[i]);
return 0;
}

题目链接

题目要求\(p-i\geq h-j+\sqrt{|i-j|}-h-i\)
\(p-i=\max\limits_{j=1}^{n}h-j+\left\lceil\sqrt{|i-j|}\right\rceil -h-i\)

因为可以观察的到\(\left\lceil\sqrt{|i-j|}\right\rceil\)取值个数是\(\mathjaxcal{O}(\sqrt{n})\)的。
所以枚举所有长度根号的上取整值,\(RMQ\)上查询对应区间的最大最小值。
即可\(\mathjaxcal{O}(n\log n+n\sqrt{n})\)时间内完成。

据说还有\(\mathjaxcal{O}(n\log n)\)的做法,咱也不会啊...
这题把高度改成小数就没法把小数改成上取整的了,这做法不就被卡了吗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int n, a[N], cnt, f[N][21], lo[N];
pair<int, int> sq[N / 100];
int sqrt(int n) {
return (int)ceil(sqrt(n * 1.0));
}

void init() {
for (int i = 1; i <= n; i++) f[i][0] = a[i];
for (int j = 1; (1 << j) <= n; j++)
for (int i = 1; i <= n - (1 << j) + 1; i++) {
f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
for (int i = 2; i <= n; i++) lo[i] = lo[i / 2] + 1;
}
int qry(int i, int j, int now, int k) {
i = max(1, i), j = min(j, n);
int len = j - i + 1;
int res = max(f[i][lo[len]], f[j - (1 << lo[len]) + 1][lo[len]]);
return res + k - a[now];
}

int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int head = 1, now = sqrt(1);
for (int i = 1; i <= n + 1; i++) {
if (sqrt(i) != now) {
sq[++cnt] = {head, i - 1};
now = sqrt(i);
head = i;
}
}
if (head != n + 1) sq[++cnt] = {head, n};
init();
for (int i = 1; i <= n; i++) {
int res = 0;
bool le = 1, re = 1;
for (int j = 1; j <= cnt; j++) {
if (!le && !re) break;
int l = sq[j].first, r = sq[j].second;
if (le) res = max(qry(i - r, i - l, i, j), res);
if (re) res = max(qry(i + l, i + r, i, j), res);
if (i - r <= 1) le = 0;
if (i + r >= n) re = 0;
}
printf("%d\n", res);
}
return 0;
}

0%