2018 Multi-University Training Contest 2-G-Naive Operations

题目链接:HDU6135-Naive Operations

(一)题面:

Problem Description

In a galaxy far, far away, there are two integer sequence a and b of length n.
b is a static permutation of 1 to n. Initially a is filled with zeroes.
There are two kind of operations:
1. add l r: add one for al,al+1...ar
2. query l r: query ∑ri=l⌊ai/bi⌋

 

Input

There are multiple test cases, please read till the end of input file.
For each test case, in the first line, two integers n,q, representing the length of a,b and the number of queries.
In the second line, n integers separated by spaces, representing permutation b.
In the following q lines, each line is either in the form 'add l r' or 'query l r', representing an operation.
1≤n,q≤100000, 1≤l≤r≤n, there're no more than 5 test cases.

 

Output

Output the answer for each 'query', each one line.

 

Sample Input

5 12
1 5 2 4 3
add 1 4
query 1 4
add 2 5
query 2 5
add 3 5
query 1 5
add 2 4
query 1 4
add 2 5
query 2 5
add 2 2
query 1 5

 

Sample Output

1
1
2
4
4
6

 

(二)题意:

给定两个序列A和B,其中A序列中的元素的初始值全为0,B序列的值已知。现有两种操作add和query。add l r操作要求将序列A中区间[l,r]之间的每个元素值加一。query l r操作查询A序列和B序列在区间[l,r]中对应位置上的值的比值(取下整)的和。并输出结果。

 

(三)题解:

对于这种题目,直观上就是线段树进行求解,但是粗看之下这个体又不是那么好做,如果对于每次query操作都更新到叶节点,那么复杂度最坏达到qnlogn,还没有朴素的做法好,但是仔细分析还是可以得到一些很有用的信息。

考虑对区间[l,r]进行add操作时,该区间求和值的变化情况:显然只有当序列A中的某个(些)值加一以后等于数组B中对应位置上的数值的整倍数时,该区间的值才会发生改变。那么对于所有q个操作而言,区间的值的改变次数最多为多少呢?由于B数组的值为1~n的一个排列,所以对于q次操作,区间的值的改变次数最多为:q*(1+1/2+1/3+...+1/n)≈q(lnn+γ)。也就是说最多需要需要修改区间的和约q*lnn次,而每一修改的代价为O(logn),故总的复杂度为O(q*lnn*logn)。这样就可以做了。

接下来就是维护一个区间什么时候需要进行修改操作。如果直接考虑上述的做法,实际上实现起来并不方便。我们可以这么考虑,对于序列A的一个区间的各个位置的值加一,反过来可以看作序列B中对应区间的各个位置的值减一,那么当序列B中的该区间的某个值减到0时,说明该区间的和需要加一,然后把该位置的值重新赋值为初始值即可。所以我们就只需要维护B序列的区间的最小值,当区间的最小值为0时进行一次修改操作,并维护一下区间和即可。

 

(四)代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e5+10;
int b[maxn],ans;
struct TreeNode{
    int l,r,lazy,_min,sum;
    int mid(){return (l+r)>>1;}
}tree[maxn<<2];
void Push_Down(int i){
    if(tree[i].lazy){
        tree[i<<1].lazy+=tree[i].lazy;
        tree[i<<1]._min+=tree[i].lazy;
        tree[i<<1|1].lazy+=tree[i].lazy;
        tree[i<<1|1]._min+=tree[i].lazy;
        tree[i].lazy=0;
    }
}
void BuildTree(int i,int l,int r){
    tree[i].l=l;tree[i].r=r;
    tree[i]._min=tree[i].lazy=tree[i].sum=0;
    if(l==r){
        scanf("%d",&b[l]);
        tree[i]._min=b[l];
        return;
    }
    int m=tree[i].mid();
    BuildTree(i<<1,l,m);
    BuildTree(i<<1|1,m+1,r);
    tree[i]._min=min(tree[i<<1]._min,tree[i<<1|1]._min);
}
int UpdateTree(int i,int l,int r){             //区间-1并返回修改后的最小值
    if(l<=tree[i].l&&tree[i].r<=r){
        tree[i]._min--;
        tree[i].lazy--;
        return tree[i]._min;
    }
    Push_Down(i);
    int m=tree[i].mid(),Min;
    if(r<=m)    Min=UpdateTree(i<<1,l,r);
    else if(l>m)Min=UpdateTree(i<<1|1,l,r);
    else        Min=min(UpdateTree(i<<1,l,m),UpdateTree(i<<1|1,m+1,r));
    return tree[i]._min=min(tree[i]._min,Min);
}
void Modify(int i,int l,int r){                //如果最小值为0,进行最小值与和的修改
    if(tree[i].l==tree[i].r){
        tree[i].sum++;
        tree[i]._min=b[l];
        return;
    }
    Push_Down(i);
    int m=tree[i].mid();
    if(r<=m)Modify(i<<1,l,r);
    else if(l>m)Modify(i<<1|1,l,r);
    else{
        if(!tree[i<<1]._min)Modify(i<<1,l,m);
        if(!tree[i<<1|1]._min)Modify(i<<1|1,m+1,r);
    }
    tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
    tree[i]._min=min(tree[i<<1]._min,tree[i<<1|1]._min);
}
LL QueryTree(int i,int l,int r){               //查询区间和
    if(l<=tree[i].l&&tree[i].r<=r)return tree[i].sum;
    Push_Down(i);
    int m=tree[i].mid();
    if(r<=m)return QueryTree(i<<1,l,r);
    else if(l>m)return QueryTree(i<<1|1,l,r);
    else return QueryTree(i<<1,l,m)*1ll+QueryTree(i<<1|1,m+1,r);
}
int main(){
    freopen("1007.in","r",stdin);
    int n,q,l,r;
    char str[20];
    while(~scanf("%d%d",&n,&q)){
        BuildTree(1,1,n);
        for(int i=0;i<q;i++){
            scanf("%s%d%d",str,&l,&r);
            if(str[0]=='a'){
                if(!UpdateTree(1,l,r))Modify(1,l,r);
            }
            else printf("%lld\n",QueryTree(1,l,r));
        }
    }
    return 0;
}

 

(五)总结:

这个题会做的人好多啊,没做出来有点难受QWQ。

赛场上貌似想到了一点边边,但是没能搞出来(TLE)。

线段树有关的操作还是十分灵活的,需要仔细分析、求解。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值