Codeforces Round 958 (Div. 2) E
分块解法··········
解法
- 首先不删点的情况
- 令 l[i] 为小于a[i]的左边第一个下标(没有a[j]<0,l[i]即为0),r[i]为大于a[i]的右边第一个下标,(没有a[j]<a[i],r[i]即为n+1)
- 那么一个区间的最小值是a[i]的对于答案的贡献为a[i](r[i]-i)(i-l[i])
- 考虑删除点的情况,首先l[i]为该点的点的左边界会变为小于a[i]的左边第二个下标,此时删除l[i]对答案的贡献增加a[i] * (r[i] - i) * (i - l1[i] - 1) - ct[i];(l1[i]为小于a[i]的左边的第二个下标);对于r[i],dui答案贡献增加a[i] * (r1[i] - i - 1) * (i - l[i]) - ct[i]。
- 删除点还有一部分变化为左边界小于该点的左区间减少1,删去在[l[i],i]区间的点对答案贡献减少a[i] * (r[i] - i);右边界大于该点的右区间减少1,删去在[i,r[i]]区间的点对答案贡献减少a[i] * (i - l[i])。
思路阐明结束,具体实现如下:
- 找下标部分运用分块,存储每个块的最小值,最多遍历2*sqrt(n)次(找两个下标)
- 删除点区间贡献部分可以使用树状数组解决。
- 总时间复杂度nlogn+nsqrt(n)好友最慢,但是思维量很小
- P.S. 我永远热爱暴力数据结构.
代码部分:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define lowbit(x) (x & -x)
const int N = 5e5 + 5;
int n, a[N];
int l[N], r[N], l1[N], r1[N];
int ct[N], del[N], mn[N];
int sq, tot, bl[N];
void init()
{
sq = sqrt(n);
tot = ceil(n * 1.0 / sq); // 块数
for (int i = 1; i <= tot; i++)
mn[i] = n + 1;
for (int i = 1; i <= tot; i++)
for (int j = (i - 1) * sq + 1; j <= min(n, sq * i); j++)
{
bl[j] = i;
mn[i] = min(mn[i], a[j]);
}
} // 分块
int tr[N];
void add(int pos, int val)
{
for (int i = pos; i <= n; i += lowbit(i))
tr[i] += val;
}
int query(int pos)
{
int ret = 0;
for (int i = pos; i; i -= lowbit(i))
ret += tr[i];
return ret;
}
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i], del[i] = 0;
init();
for (int i = 1; i <= n; i++)
{
int js = 0;
l[i] = l1[i] = 0;
r[i] = r1[i] = n + 1;
for (int j = i - 1; j >= (bl[i] - 1) * sq + 1; j--)
if (a[j] < a[i])
if (js == 0)
{
l[i] = j;
js++;
}
else if (js == 1)
{
l1[i] = j;
js++;
}
for (int j = bl[i] - 1; j >= 1; j--)
{
if (js == 2)
break;
if (mn[j] < a[i])
{
for (int k = sq * j; k >= sq * (j - 1) + 1; k--) // 贪心
if (a[k] < a[i])
if (js == 0)
{
l[i] = k;
js++;
}
else if (js == 1)
{
l1[i] = k;
js++;
}
}
} // l,l1
js = 0;
for (int j = i + 1; j <= min(n, bl[i] * sq); j++)
if (a[j] < a[i])
if (js == 0)
{
r[i] = j;
js++;
}
else if (js == 1)
{
r1[i] = j;
js++;
}
for (int j = bl[i] + 1; j <= tot; j++)
{
if (js == 2)
break;
if (mn[j] < a[i])
{
for (int k = sq * (j - 1) + 1; k <= min(n, sq * j); k++)
if (a[k] < a[i])
if (js == 0)
{
r[i] = k;
js++;
}
else if (js == 1)
{
r1[i] = k;
js++;
}
}
} // r,r1
}
// for (int i = 1; i <= n; i++)
// cout << l[i] << ' ' << r[i] << endl;
int ans = 0;
for (int i = 1; i <= n; i++)
{
ans += a[i] * (r[i] - i) * (i - l[i]);
ct[i] = a[i] * (r[i] - i) * (i - l[i]);
if (l[i])
del[l[i]] -= a[i] * (r[i] - i) * (i - l1[i] - 1) - ct[i];
if (r[i] != n + 1)
del[r[i]] -= a[i] * (r1[i] - i - 1) * (i - l[i]) - ct[i];
}
for (int i = 1; i <= n; i++)
tr[i] = 0;
for (int i = 1; i <= n; i++)
{
add(l[i] + 1, a[i] * (r[i] - i));
add(i + 1, -a[i] * (r[i] - i));
}
for (int i = 1; i <= n; i++)
del[i] += query(i) - a[i] * (r[i] - i);
for (int i = 1; i <= n; i++)
tr[i] = 0;
for (int i = 1; i <= n; i++)
{
add(n + 1 - r[i] + 1, a[i] * (i - l[i]));
add(n + 2 - i, -a[i] * (i - l[i]));
}
for (int i = 1; i <= n; i++)
del[i] += query(n + 1 - i) - a[i] * (i - l[i]);
for (int i = 1; i <= n; i++)
cout << ans - ct[i] - del[i] << ' ';
cout << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
solve();
}
AC提交记录
提交记录