【线段树 && 区间加 1 和区间求 a[i]/b[i] 的和】HDU - 6315 Naive Operations

Step1 Problem:

给你 n 个数,数组 a[] 初始化为 0,给你数组 b[](n 的全排列),进行 q 次操作,每次操作分为:
add L R: 数组 a[] 区间 L 到 R 的值都加 1
query L R:求区间 L 到 R,a[i]/b[i] 的和
数据范围:
1 <= n <= 1e5, 1 <= q <= 1e5, 1 <= L, R <= n.

Step2 Ideas:

对于每个 i,想要有贡献就需要 a[i] > b[i],相当于 i 这个位置需要 add b[i] 次就贡献 1,那么我们让 a[i] 初始为 b[i],每 add 一次 a[i] - 1,当 a[i] = 0 的时候就贡献了 1,再让 a[i] 变为 b[i]。核心点是想到减法,比赛用的加法然后mod b[i],结果怎么优化都超时
核心的两个条件:数组 b[] 是全排列,add 每次只加 1。
假设 n = 1e5, q = 1e5. 每次 add 区间 1~n,
贡献次数 = q/1 + q/2 + q/3 + q/4 + … + q/n,这个数和 n*log(n) 差不多。
所以我们可以维护区间最小值和区间减法,当最小值 = 0 的时候,我们就更新贡献值同时将 0 变为 b[i](更新次数也就 n*log(n) 次)。

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
#define lson root<<1
#define rson root<<1|1
#define MID int mid = (l+r)/2
const int inf = 0x3f3f3f3f;
const int N = 1e5+5;
struct node
{
    int ans, Min, lazy;
};
node tree[N<<2];
int a[N], n;
node Merge(node x, node y)
{
    node t;
    t.Min = min(x.Min, y.Min);
    t.ans = x.ans + y.ans;
    t.lazy = 0;
    return t;
}
void creat(int root, int l, int r)
{
    tree[root].lazy = 0;
    if(l == r) {
        tree[root].ans = 0;
        tree[root].Min = a[l];
        return ;
    }
    MID;
    creat(lson, l, mid);
    creat(rson, mid+1, r);
    tree[root] = Merge(tree[lson], tree[rson]);
}
void pushdown(int root, int l, int r)
{
    if(tree[root].lazy) {
        tree[lson].lazy += tree[root].lazy;
        tree[lson].Min -= tree[root].lazy;
        tree[rson].lazy += tree[root].lazy;
        tree[rson].Min -= tree[root].lazy;
        tree[root].lazy = 0;
    }
}
void updata(int root, int l, int r, int L, int R)
{
    if(L <= l && r <= R)
    {
        tree[root].Min--;
        tree[root].lazy++;
        return ;
    }
    pushdown(root, l, r);
    MID;
    if(mid >= L) updata(lson, l, mid, L, R);
    if(mid < R) updata(rson, mid+1, r, L, R);
    tree[root] = Merge(tree[lson], tree[rson]);
}
void updata_Min(int root, int l, int r)
{
    if(l == r) {
        tree[root].Min = a[l];
        tree[root].ans++;
        return;
    }
    MID;
    pushdown(root, l, r);
    if(tree[lson].Min < tree[rson].Min) updata_Min(lson, l, mid);
    else updata_Min(rson, mid+1, r);
    tree[root] = Merge(tree[lson], tree[rson]);
}
int query(int root, int l, int r, int L, int R)
{
    if(L <= l && r <= R)
    {
        return tree[root].ans;
    }
    int ret = 0;
    MID;
    if(mid >= L) ret += query(lson, l, mid, L, R);
    if(mid < R) ret += query(rson, mid+1, r, L, R);
    return ret;
}
void show()
{
    for(int i = 1; i <= 4*n; i++)
        printf("%d %d %d\n", tree[i].ans, tree[i].lazy, tree[i].ans);
}
int main()
{
    int m;
    while(~scanf("%d %d", &n, &m))
    {
        for(int i = 1; i <= n; i++)
            scanf("%d", a+i);
        creat(1, 1, n);
        //show();
        char ok[15];
        int L, R;
        while(m--)
        {
            scanf("%s %d %d", ok, &L, &R);
            if(ok[0] == 'a') {
                updata(1, 1, n, L, R);
                while(tree[1].Min == 0) {
                    updata_Min(1, 1, n);
                }
            }
            else if(ok[0] == 'q') {
                printf("%d\n", query(1, 1, n, L, R));
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值