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