Codeforces Round 958 (Div. 2) E. Range Minimum Sum

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提交记录

提交记录

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值