频道栏目
首页 > 考试 > 其他 > 正文

【HDU 5828】Rikka with Sequence(线段树)

2016-08-22 09:38:10         来源:ACMer'  
收藏   我要投稿
Time Limit: 6000/3000 MS (Java/Others)Memory Limit: 65536/65536 K (Java/Others)


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 2016 Multi-University Training Contest 8

 

题目大意:
区间三种操作。
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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include
#include 
#define LL long long
#define Pr pair
#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;
}
相关TAG标签 线段
上一篇:hud4273--poj3862+三维凸包模板题+凸包重心到表面的最小距离
下一篇:剑指Offer笔记(JAVA版)(二)
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站