洛谷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);
}
}

浙公网安备 33010602011771号