Naive Operations(线段树)

Naive Operations

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 502768/502768 K (Java/Others)
Total Submission(s): 1042    Accepted Submission(s): 413


 

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

 

Source

2018 Multi-University Training Contest 2

转自这里

思路:假设n次操作都是add 1 n,那么所有的 ai/bi的和sum=n/1+n/2+n/3+....+1  。所有sum近似等于nlog(n)。

也就是说,我们实际上只需要更新nlog(n)次就行了,也就是能对答案产生贡献的时候我们再去更新。

那么利用线段树。

节点存储三个信息 val :

1.val 表示当前ai还差val就可以到达bi,节点维护最小的val值,若区间最小的val>0,说明没有ai>=bi,也就产生不了贡献。

2.sum 表示区间的add操作次数;

3.ans 表示之前区间ai/bi的和。

对于每次add操作,sum++,val--。

对于查询操作,如果val>0,说明这个区间产生不了贡献,直接返回ans值。

如果val<=0,说明这个区间存在ai>=bi,对答案有贡献。继续往下查询,直到叶子节点。

到了叶子节点,若val<=0,说明有贡献,则原先的val=sum+val(因为add操作更新时sum++,val就--),那么现在你还差val的值达到bi,且ai加了sum次,然后更新一下答案ans,重新设置val值,把sum变为0,然后返回答案就行。

返回答案的时候,要注意更新由叶子节点改变而传递上来的信息。

线段树的作用就是减少了更新的次数,将原来要更新很多次区间,先储存在节点的sum上,

什么时候节点存的val值告诉我们必须要更新区间了,那么我们这时候才将sum分发下去到叶子节点,

那么顺带着,更新了叶子节点,回溯的时候要更新val的值。

查询的时候也是

#include <bits/stdc++.h>
using namespace std;
#define ll long long
struct tree
{
    int l,r,sum,val,ans;
}A[100010<<2];
int b[100010];
void built(int k,int l,int r)
{
    A[k].l=l,A[k].r=r;
    A[k].ans=A[k].val=A[k].sum=0;
    if(l==r){A[k].val=b[r];return;}
    built(2*k,l,(l+r)/2);
    built(2*k+1,(l+r)/2+1,r);
    A[k].val=min(A[2*k].val,A[2*k+1].val);
}
void add(int k,int l,int r,int v)
{
    if(A[k].l==l&&A[k].r==r)
    {
        A[k].sum+=v;
        A[k].val-=v;
        return;
    }
    if(A[k].sum)
    {
        add(2*k,A[2*k].l,A[2*k].r,A[k].sum);
        add(2*k+1,A[2*k+1].l,A[2*k+1].r,A[k].sum);
        A[k].sum=0;
    }
    if(r<=A[2*k].r)add(2*k,l,r,v);
    else if(l>=A[2*k+1].l) add(2*k+1,l,r,v);
    else
    {
        add(2*k,l,A[2*k].r,v);
        add(2*k+1,A[2*k+1].l,r,v);
    }
    A[k].val=min(A[2*k].val,A[2*k+1].val);
}

int ask(int k,int l,int r)
{
    if(l==A[k].l&&r==A[k].r)
    {
        if(A[k].val>0)return A[k].ans;
        if(l==r)
        {
            A[k].val+=A[k].sum;

            A[k].sum+=b[l]-A[k].val;

            A[k].ans+=A[k].sum/b[l];

            A[k].val=b[l]-A[k].sum%b[l];

            A[k].sum=0;

            return A[k].ans;
        }
    }
    if(A[k].sum)
    {
        add(2*k,A[2*k].l,A[2*k].r,A[k].sum);
        add(2*k+1,A[2*k+1].l,A[2*k+1].r,A[k].sum);
        A[k].sum=0;
    }
    int ans=0;
    if(r<=A[2*k].r)ans+=ask(2*k,l,r);
    else if(l>=A[2*k+1].l)ans+=ask(2*k+1,l,r);
    else ans+=ask(2*k,l,A[2*k].r)+ask(2*k+1,A[2*k+1].l,r);
    A[k].val=min(A[2*k].val,A[2*k+1].val);
    A[k].ans=A[2*k].ans+A[2*k+1].ans;
    return ans;
}

int main(){
    int m,n;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=n;i++)scanf("%d",&b[i]);
        built(1,1,n);
        while(m--)
        {
            char As[10];
            int l,r;
            scanf("%s%d%d",As,&l,&r);
            if(As[0]=='a')add(1,l,r,1);
            else printf("%d\n",ask(1,l,r));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/du_mingm/article/details/81214616