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

链接:https://ac.nowcoder.com/acm/contest/883/G
来源:牛客网
 

题目描述

给出一个n和长度为n的序列,

求有多少个区间[l,r]满足[SUM(L,R)/2] >= MAX(L,R)

输入

2
3
1 1 1
4
1 2 3 4

输出

3
3

题解:

 

魔鬼出题人卡时间。。标程写的很简洁也跑了600+ms。题目才给1s。要常数小才能过。

有个尺取的假做法能过,但是复杂度容易退化到O(n^2),不是正解(数据水了。跑的比正解快)

代码:

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

const int MAXN = 300005;
typedef long long ll;
typedef pair<int,int> P;

int n;
int a[MAXN];
int dp[MAXN][19];
int mm[MAXN];
ll sum[MAXN];
inline int cmp(const int &x, const int &y){
    return (a[x] > a[y]) ? x : y;
}
inline void init(int n){
    sum[0] = 0ll;
    for(int i = 1; i <= n; i++)
        dp[i][0] = i,
        sum[i] = sum[i-1] + a[i];
    for(int j = 1; j <= mm[n]; j++)
        for(int i = 1; i + (1<<j) - 1 <= n; i++)
            dp[i][j] = cmp(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}
inline int rmq(const int &x, const int &y){
    int k = mm[y-x+1];
    return cmp(dp[x][k], dp[y-(1<<k)+1][k]);
}
#define sumdiv2(x,y) ((sum[y] - sum[x-1])>>1)
ll ans;
inline void dfs(int L,int R){
    if(L >= R) return;
    int k = rmq(L,R);
    ll mx = a[k];
    if(k-L<R-k){
        register int l, r = k, mid;
        for(int i = L; i <= k; i++){
            if(sumdiv2(i,R) < mx) break;
            l = r-1, r = R;
            while(l+1<r){
                mid = (l+r)>>1;
                if(sumdiv2(i, mid) >= mx)
                    r = mid;
                else
                    l = mid;
            }
            ans += R-r+1;
        }
    }else{
        register int l = k, r, mid;
        for(int i = R; i >= k; i--){
            if(sumdiv2(L,i) < mx) break;
            r = l+1, l = L;
            while(l+1<r){
                mid = (l+r)>>1;
                if(sumdiv2(mid, i) >= mx)
                    l = mid;
                else
                    r = mid;
            }
            ans += l-L+1;
        }
    }
    dfs(L,k-1); dfs(k+1,R);
}
int main()
{
    for(int i = 2; i < MAXN; i++)
        mm[i] = ((i&(i-1))==0) ? mm[i-1]+1 : mm[i-1];
    int T; scanf("%d",&T); while(T--){
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        init(n);
        ans = 0; dfs(1,n);
        printf("%lld\n", ans);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值