1. 程式人生 > >【HDU 5828】Rikka with Sequence(線段樹)

【HDU 5828】Rikka with Sequence(線段樹)

Rikka with Sequence

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2311    Accepted Submission(s): 391


Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:

Yuta has an array A with n numbers. Then he makes m operations on it.

There are three type of operations:

1 l r x : For each i in [l,r], change A[i] to A[i]+x
2 l r : For each i in [l,r], change A[i] to
A[i]

3 l r : Yuta wants Rikka to sum up A[i] for all i in [l,r]

It is too difficult for Rikka. Can you help her?
Input The first line contains a number t(1<=t<=100), the number of the testcases. And there are no more than 5 testcases with n>1000.

For each testcase, the first line contains two numbers n,m(1<=n,m<=100000). The second line contains n numbers A[1]~A[n]. Then m lines follow, each line describe an operation.

It is guaranteed that 1<=A[i],x<=100000.
Output For each operation of type 3, print a lines contains one number – the answer of the query.
Sample Input
1 
5 5
1 2 3 4 5
1 3 5 2
2 1 4
3 2 4
2 3 5
3 1 5

Sample Output
5 
6

Author 學軍中學
Source

題目大意:
區間三種操作。
1 區間加
2 區間開根
3 區間求和

第一個和第三個用Lazy外加合併相同區間。時間很充裕
開根和區間加不一樣的是與元素本身有關。所以很爆炸的要找到每一個葉子。
如果單單這樣其實最差的複雜度只會在構造出這種序列後的第一次開根。
但是有一種特殊情況,類似(2,3)+6(8,9)sqrt(2,3)
這樣如果序列是交錯排列的2 3 2 3 2 3 2 3,然後對區間不斷+6 開根 +6 開根,就退化到了單次O(n)

這組資料是在賽後加的,當時一片片TLE……現在一瞅過了不少,然後百度了一下發現有人發了更優美的題解,然後就來補了一下。

開根的處理就是除了維護區間和外,再維護一個區間最大mx與最小mn。
當區間mx-mn <= 1 && sqrt(mx)-sqrt(mn) <= 1,就可以cut了,因為開根前後差值是一樣的,就變為了區間減法,lazy標記一下就可以了

另發現絕版輸入掛,常數優化絕妙

程式碼如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <ctime>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define frd(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 112345;
const int mod = 1e9+7;
const double eps = 1e-8;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
    if(head==tail) {
        int l=fread(buffer,1,BufferSize,stdin);
        tail=(head=buffer)+l;
    }
    return *head++;
}
inline int read() {
    int x=0,f=1;char c=Getchar();
    for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
    return x*f;
}

LL sum[4*msz],mx[4*msz],mn[4*msz],lz[4*msz];

void down(int root,int l,int r)
{
    if(!lz[root]) return;
    int mid = (l+r)>>1;
    mx[root<<1] += lz[root];
    mx[root<<1|1] += lz[root];

    mn[root<<1] += lz[root];
    mn[root<<1|1] += lz[root];

    sum[root<<1] += lz[root]*(mid-l+1);
    sum[root<<1|1] += lz[root]*(r-mid);

    lz[root<<1] += lz[root];
    lz[root<<1|1] += lz[root];
    lz[root] = 0;
}

void up(int root)
{
    sum[root] = sum[root<<1]+sum[root<<1|1];
    mx[root] = max(mx[root<<1],mx[root<<1|1]);
    mn[root] = min(mn[root<<1],mn[root<<1|1]);
}

void Add(int root,int l,int r,int ll,int rr,LL ad)
{
    if(l == ll && r == rr)
    {
        lz[root] += ad;
        sum[root] += ad*(r-l+1);
        mx[root] += ad;
        mn[root] += ad;
        return;
    }

    int mid = (l+r)>>1;

    down(root,l,r);

    if(mid >= rr) Add(root<<1,l,mid,ll,rr,ad);
    else if(mid+1 <= ll) Add(root<<1|1,mid+1,r,ll,rr,ad);
    else
    {
        Add(root<<1,l,mid,ll,mid,ad);
        Add(root<<1|1,mid+1,r,mid+1,rr,ad);
    }

    up(root);
}

LL Sum(int root,int l,int r,int ll,int rr)
{
    if(l == ll && r == rr)
    {
        return sum[root];
    }

    int mid = (l+r)>>1;
    down(root,l,r);

    if(mid >= rr) return Sum(root<<1,l,mid,ll,rr);
    else if(mid+1 <= ll) return Sum(root<<1|1,mid+1,r,ll,rr);
    return Sum(root<<1,l,mid,ll,mid)+Sum(root<<1|1,mid+1,r,mid+1,rr);
}

void Sqrt(int root,int l,int r,int ll,int rr)
{
    //printf("[%d,%d] mx%lld mn%lld\n",l,r,mx[root],mn[root]);
    if(l == ll && r == rr)
    {
        LL cut;
        if(mx[root] == mn[root])
        {
            cut = mx[root]-floor(sqrt(mx[root]*1.0));
            lz[root] -= cut;
            sum[root] -= cut*(r-l+1);
            mx[root] = mn[root] = mx[root]-cut;
            return;
        }
        else if(mx[root]-mn[root] == 1)
        {
            LL t1,t2;
            t1 = floor(sqrt(mx[root]*1.0));
            t2 = floor(sqrt(mn[root]*1.0));
            if(t1-t2 == 1)
            {
                cut = mx[root]-t1;
                lz[root] -= cut;
                sum[root] -= cut*(r-l+1);
                mx[root] = t1;
                mn[root] = t2;
                return;
            }
        }
    }

    int mid = (l+r)>>1;
    down(root,l,r);

    if(mid >= rr) Sqrt(root<<1,l,mid,ll,rr);
    else if(mid+1 <= ll) Sqrt(root<<1|1,mid+1,r,ll,rr);
    else
    {
        Sqrt(root<<1,l,mid,ll,mid);
        Sqrt(root<<1|1,mid+1,r,mid+1,rr);
    }

    up(root);
}

void init(int root,int l,int r)
{
    lz[root] = 0;
    if(l == r)
    {
        sum[root] = read();
        mx[root] = mn[root] = sum[root];
        return;
    }

    int mid = (l+r)>>1;
    init(root<<1,l,mid);
    init(root<<1|1,mid+1,r);
    up(root);
}


int main()
{
    //frd("1008.in");
    //fwrite("m.out");

    int t,opt,l,r,x,n,q;

    t = read();

    while(t--)
    {
        n = read();
        q = read();

        init(1,1,n);

        while(q--)
        {
            opt = read();
            l = read();
            r = read();
            if(opt == 1)
            {
                x = read();
                Add(1,1,n,l,r,x);
            }
            else if(opt == 2)
            {
                Sqrt(1,1,n,l,r);
            }
            else printf("%lld\n",Sum(1,1,n,l,r));
        }
    }

    return 0;
}