SPOJ 2713 Can you answer these queries IV(线段树)

来源:互联网 发布:qq象棋作弊软件 编辑:程序博客网 时间:2024/06/05 16:34
Can you answer these queries IV
#tree

You are given a sequence A of N(N <= 100,000) positive integers. There sum will be less than 1018. On this sequence you have to apply M (M <= 100,000) operations:

(A) For given x,y, for each elements between the x-th and the y-th ones (inclusively, counting from 1), modify it to its positive square root (rounded down to the nearest integer).

(B) For given x,y, query the sum of all the elements between the x-th and the y-th ones (inclusively, counting from 1) in the sequence.

Input

Multiple test cases, please proceed them one by one. Input terminates by EOF.

For each test case:

The first line contains an integer N. The following line contains N integers, representing the sequence A1..AN.
The third line contains an integer M. The next M lines contain the operations in the form "i x y".i=0 denotes the modify operation, i=1 denotes the query operation.

Output

For each test case:

Output the case number (counting from 1) in the first line of output. Then for each query, print an integer as the problem required.

Print an blank line after each test case.

See the sample output for more details.

Example

Input:51 2 3 4 551 2 40 2 41 2 40 4 51 1 5410 10 10 1031 1 40 2 31 1 4Output:Case #1:946Case #2:4026



        线段树的题真是好多,像这个系列,一直可以到Ⅵ、Ⅶ。
        Ⅲ我就不说了,就是在Ⅰ的基础上加一个单点修改,Ⅳ可就几乎是完全不同了,它是要区间开根号,然后询问区间和。可能你会说,区间开根号怎么做,如何维护区间和?因为区间开根号后,区间和的值显然不是原值开根号(叶子节点除外)。开始我还想了用对数去解决这个问题,对于对数,对真数开根号,等于对对数除以2。但是对数的和并不是想要的区间和。
        其实呢,这题不用考虑太多。考虑一个64位整数,它最多开7次根号就会等于1(2^6=64),这也就意味着,每一个数字最多能开7次根号,再往后就没有意义了。对应10W个操作的数据范围,最多也就开70W次根号。所以说,对于修改,我们不用顾虑,直接暴力修改即可。然而,这个暴力也是有点讲究的,并不是说直接用循环O(n)的去修改。我们还是得在线段树上修改,先找到对应区间,如果区间的和等于区间长,那么直接退出,即该区间已经全部是1,不需要进行修改。否则,继续拆分区间,直到找到叶子节点,这是再单点暴力更新。看似没什么,其实在判定区间长是否等于区间和的时候能够升很多时间。
        这个时间复杂度的话,就得均摊来算了,对于每一个修改操作的具体复杂度不能确定,但是总的复杂度是O(7*NlogN),N个点最多进行7次修改,每次单点修改时间代价是logN,所以总的还是在O(NlogN)的级别。具体代码如下:
#include<bits/stdc++.h>#define LL long long#define N 201000using namespace std;LL a[N];int n;struct ST{struct node{LL sum;int l,r;} tree[N*4];inline void push_up(int i){    tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;}inline void build(int i,int l,int r){tree[i].r=r;tree[i].l=l;if (l==r){tree[i].sum=a[l];return;}LL mid=(l+r)>>1;build(i<<1,l,mid);build(i<<1|1,mid+1,r);push_up(i);}    inline LL getsum(int i,int l,int r){if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].sum;LL mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) return getsum(i<<1,l,r);else if (mid<l) return getsum(i<<1|1,l,r);else return getsum(i<<1,l,mid)+getsum(i<<1|1,mid+1,r);}inline void update(int i,int l,int r){        if (tree[i].r-tree[i].l+1==tree[i].sum) return;//区间长等于区间和,说明全部为1,没必要再开根号        if (tree[i].r==tree[i].l)//否则直接暴力到叶子节点再修改        {            tree[i].sum=(int)sqrt(tree[i].sum);            return;        }        LL mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) update(i<<1,l,r);else if (mid<l) update(i<<1|1,l,r);else{            update(i<<1,l,mid);            update(i<<1|1,mid+1,r);}        push_up(i);}} seg;int main(){    int T_T=0;    while(~scanf("%d",&n))    {        printf("Case #%d:\n",++T_T);        for(int i=1;i<=n;i++)            scanf("%lld",&a[i]);        seg.build(1,1,n);        int q; scanf("%d",&q);        while(q--)        {            int op,l,r;            scanf("%d%d%d",&op,&l,&r);            if (l>r) swap(l,r);            if (op==1) printf("%lld\n",seg.getsum(1,l,r));                  else seg.update(1,l,r);        }        puts("");    }    return 0;}

阅读全文
0 0
原创粉丝点击