洛谷P4145【上帝造题的七分钟2】

神奇线段树

(线段树做法,可以并查集做,并查集类似于BZOJ疯狂的馒头) 查询操作是一个简单的线段树求和操作 重点在修改操作

考虑到当一个单点的值如果为1或0时,这个点开根号之后没有意义不会改变值。因此我们对这样的点作一个标记,如果碰到这个点就不修改。

对于一般的结点,则当两个儿子都被做了标记,那么就给这个点加标记,以后不对该结点进行修改操作(如果操作之后其sum值依然不会变)

平均时间复杂度为O(nlogn)

代码:

#include<cstdio>
#include<iostream>
#include<cmath>
#define ll long long 
#define maxn 100005
using namespace std;

void read(ll &x) 
{

bool flag = false;
char c=getchar();
while((c<'0' || c>'9') && c != '-')c=getchar();
if (c == '-') {
    x = 0;
    flag = true;
} else {
    x = c - '0';
}
c = getchar();
while(c>='0' && c<='9') {
    x=x*10+c-'0';
    c=getchar();
}
x = flag ? -x : x;
return;
} 

struct node{
ll a,b,c;
bool lazy;
}z[maxn<<3];
ll n,a[maxn];

void maketree(ll p,ll l,ll r)
{
z[p].a=l; z[p].b=r;
if(l==r){ z[p].c=a[l]; if(z[p].c==1||z[p].c==0)z[p].lazy=1; return; }
maketree(p<<1,l,((l+r)>>1));
maketree(p<<1|1,((l+r)>>1)+1,r);
z[p].c=z[p<<1].c+z[p<<1|1].c;
    if(z[p<<1].lazy==1&&z[p<<1|1].lazy==1) z[p].lazy=1;
}

void update(ll p,ll &x,ll &y)
{

if(z[p].a==z[p].b)
{
    z[p].c=floor(sqrt(z[p].c));
    if(z[p].c==1||z[p].c==0){ z[p].lazy=1; }
    return;
}

if(y>=z[p<<1].a&&x<=z[p<<1].b&&(!z[p<<1].lazy))

update(p<<1,x,y);

if(y>=z[p<<1|1].a&&x<=z[p<<1|1].b&&(!z[p<<1|1].lazy)) update(p<<1|1,x,y);
z[p].c=z[p<<1].c+z[p<<1|1].c;
if(z[p<<1].lazy==1&&z[p<<1|1].lazy==1) z[p].lazy=1;
}

ll getsum(ll p,ll &x,ll &y)
{
if(x<=z[p].a&&z[p].b<=y)
{
    return z[p].c;
}
ll sum=0;
if(y>=z[p<<1].a&&x<=z[p<<1].b) sum+=getsum(p<<1,x,y);
if(y>=z[p<<1|1].a&&x<=z[p<<1|1].b) sum+=getsum(p<<1|1,x,y);
return sum;
}

int main()
{
ll i;
read(n);
for(i=1;i<=n;i++)
{
    read(a[i]);
}
maketree(1,1,n);
ll m,ask,x,y;
read(m);
while(m--)
{
    read(ask); read(x); read(y);
    if(x>y) swap(x,y);
    if(ask==1)
    {
        printf("%lld\n",getsum(1,x,y));
    }
    else update(1,x,y);
}
}

 

 

posted @ 2018-03-20 17:16  Newuser233  阅读(6)  评论(0)    收藏  举报