2019牛客暑期多校训练营(第三场)G Removing Stones (分治+RMQ)

题目链接
题解:
如果一段区间它满足 m a x ( l , r ) ≤ ⌊ s u m ( l , r ) / 2 ⌋ max(l, r)\leq \lfloor sum(l, r)/2\rfloor max(l,r)sum(l,r)/2,则代表这个区间符合要求。
那我们就可以对这个区间进行分治,分为最大值左边的区间和最大值右边的区间。那么包括最大值的区间怎么办呢?我们可以枚举长度较小的那个区间的值作为一个端点,二分查找另外一个区间的值作为另外一个端点,计算包含最大值并且符合条件的区间个数。例如最大值左边的区间长度比最大值右边的区间长度大,那就枚举左边的区间作为左端点 l l l ,然后二分查找右边符合条件的最左的右端点 r r r ,假设区间右端点为 R R R,则答案就是 R − r + 1 R-r+1 Rr+1。接着继续分治即可。
查询区间最大值即 RMQ 可以用 ST表解决(注意空间不要开太大,会T)。
代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define INF 0x3f3f3f3f3f3f3f3f
#define P pair<ll, int>
#define debug(x) cout << (#x) << ": " << (x) << " "
#define fastio ios::sync_with_stdio(false), cin.tie(0)
const ull mod = 1e9 + 7;
const int M = 15 + 10;
const int N = 300000 + 10;

int t;
int n, a[N];
ll sum[N];
int lg2[N], id[N][M];

inline void ST_pre()
{
    for(int i = 1; i <= n; i ++) { id[i][0] = i; }
    for(int j = 1; j <= lg2[n]; j ++) {
        for(int i = 1; i <= n - (1<<j) + 1; i ++) {
            if(a[id[i][j - 1]] > a[id[i+(1<<(j-1))][j-1]]) {
                id[i][j] = id[i][j - 1];
            } else {
                id[i][j] = id[i+(1<<(j-1))][j-1];
            }
        }
    }
}

inline int ST_query(int l, int r)
{
    int k = lg2[r - l + 1];
    if(a[id[l][k]] > a[id[r-(1<<k)+1][k]]) {
        return id[l][k];
    }
    return id[r-(1<<k)+1][k];
}

ll deal(int L, int R)
{
    if(L >= R) return 0;

    int k = ST_query(L, R);
    ll ans = 0, mx = a[k];
    if(k - L < R - k) {
        for(int i = L; i <= k; i ++) {
            int l = k, r = R;
            if(mx > (sum[r] - sum[i - 1]) >> 1) break;
            while(l < r) {
                int mid = (l + r) >> 1;
                if(mx <= (sum[mid] - sum[i - 1]) >> 1) r = mid;
                else l = mid + 1;
            }
            ans += R - l + 1;
        }
    } else {
        for(int i = R; i >= k; i --) {
            int l = L, r = k;
            if(mx > (sum[i] - sum[L - 1]) >> 1) break;
            while(l < r) {
                int mid = (l + r + 1) >> 1;
                if(mx <= (sum[i] - sum[mid - 1]) >> 1) l = mid;
                else r = mid - 1;
            }
            ans += r - L + 1;
        }
    }
    ans += deal(L, k - 1);
    ans += deal(k + 1, R);
    return ans;
}

signed main()
{
    for(int i = 1; i < N; i ++) { lg2[i] = log2(i); }
    scanf("%d", &t);
    while(t --) {
        scanf("%d", &n);
        for(int i = 1; i <= n; i ++) {
            scanf("%d", &a[i]);
            sum[i] = sum[i - 1] + a[i];
        }
        ST_pre();

        printf("%lld\n", deal(1, n));
    }
    return 0;
}

/*

  Rejoicing in hope, patient in tribulation.

*/

/*
             ,----------------,              ,---------,
        ,-----------------------,          ,"        ,"|
      ,"                      ,"|        ,"        ,"  |
     +-----------------------+  |      ,"        ,"    |
     |  .-----------------.  |  |     +---------+      |
     |  |                 |  |  |     | -==----`|      |
     |  |                 |  |  |     |         |      |
     |  |    Accepted _   |  |  |/----|`---=    |      |
     |  |                 |  |  |   ,/|==== OOO |      ;
     |  |                 |  |  |  // |(((( [33]|    ,"
     |  `-----------------`  |," .//| |((((     |  ,"
     +-----------------------+  //  | |         |,"
        /_)______________(_/  //`   | +---------+
   ___________________________/___  `,
  /  oooooooooooooooo  .o.  oooo /,   \,"-----------
 / ==ooooooooooooooo==.o.  ooo= //   ,`\--{)B     ,"
/_==__==========__==_ooo__ooo=_/`   /___________,"

*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值