单点修改区间查询
struct tree{ int l,r; long long dat; }t[1000000]; int n,m,a[1000000]; long long inf=0x7fffffff; void build(int p,int l,int r){ t[p].l=l,t[p].r=r; if(l==r){t[p].dat=a[l];return;} int mid=(l+r)/2; build(p*2,l,mid); build(p*2+1,mid+1,r); t[p].dat=max(t[p*2].dat,t[p*2+1].dat); } void change(int p,int x,int v){ if(t[p].l==t[p].r){t[p].dat=v;return;} int mid=(t[p].l+t[p].r)/2; if(x<mid)change(p*2,x,v); else change(p*2+1,x,v); t[p].dat=max(t[p*2].dat,t[p*2+1].dat); } long long ask(int p,int l,int r){ if(l<=t[p].l&&r>=t[p].r)return t[p].dat; int mid(t[p].l+t[p].r)/2; long long val=-inf; if(l<=mid)val=max(val,ask(p*2,l,r)); if(r>mid)val=max(val,ask(p*2+1,l,r)); return val; }
例题 i hate it
#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct tree{ int l,r; long long dat; }t[8000000]; int n,m;long long a[8000000]; int len,w; long long inf=0x7fffffff; void build(int p,int l,int r){ t[p].l=l,t[p].r=r; if(l==r){t[p].dat=a[l];return;} int mid=(l+r)/2; build(p*2,l,mid); build(p*2+1,mid+1,r); t[p].dat=max(t[p*2].dat,t[p*2+1].dat); } void change(int p,int x,int v){ if(t[p].l==t[p].r){t[p].dat=v;return;} int mid=(t[p].l+t[p].r)/2; if(x<=mid)change(p*2,x,v); else change(p*2+1,x,v); t[p].dat=max(t[p*2].dat,t[p*2+1].dat); } long long ask(int p,int l,int r){ if(l<=t[p].l&&r>=t[p].r)return t[p].dat; int mid=(t[p].l+t[p].r)/2; long long val=-inf; if(l<=mid)val=max(val,ask(p*2,l,r)); if(r>mid)val=max(val,ask(p*2+1,l,r)); return val; } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n); for(i=1;i<=m;i++) { char s[2]; scanf("%s",s); int p,o; scanf("%d%d",&p,&o); if(s[0]=='Q'){ printf("%d ",ask(1,p,o)); } if(s[0]=='U'){ change(1,p,o); } } return 0; }
区间修改区间查询 线段树1
#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct shu{ int l,r,dat; long long sum,add; }t[4000000]; int a[1000000],n,m; void build(int p,int l,int r){ t[p].l=l,t[p].r=r; if(l==r){ t[p].sum=a[l]; return; } int mid=l+r>>1; build(p*2,l,mid); build(p*2+1,mid+1,r); t[p].sum=t[p*2].sum+t[p*2+1].sum; } void spread(int p){ if(t[p].add){//如果懒标记不为0,就将其下传,修改左右儿子维护的值 t[p*2].sum+=t[p].add*(t[p*2].r-t[p*2].l+1); t[p*2+1].sum+=t[p].add*(t[p*2+1].r-t[p*2+1].l+1); t[p*2].add+=t[p].add;//为该节点的左右儿子打上标记 t[p*2+1].add+=t[p].add; t[p].add=0;//下传之后将该节点的懒标记清0 } } void change(int p,int x,int y,int z){ if(x<=t[p].l && y>=t[p].r){//被覆盖的话,就对其进行修改 t[p].sum+=(long long)z*(t[p].r-t[p].l+1); t[p].add+=z;//打上懒标记 return; } spread(p);//如果发现没有被覆盖,那就需要继续向下找,考虑儿子所维护的区间可能因为懒标记的存在而没有修改,因此将懒标记下放 int mid=t[p].l+t[p].r>>1; if(x<=mid) change(p*2,x,y,z);//如果要修改的区间覆盖了左儿子,就修改左儿子 if(y>mid) change(p*2+1,x,y,z);//右儿子同理 t[p].sum=t[p*2].sum+t[p*2+1].sum;//最终维护的值等于左儿子的值+右儿子的值 } long long ask(int p,int x,int y){ if(x<=t[p].l && y>=t[p].r) return t[p].sum;//如果被覆盖,就返回维护的值 spread(p);//下传懒标记,并查询左右儿子 int mid=t[p].l+t[p].r>>1; long long ans=0; if(x<=mid) ans+=ask(p*2,x,y); if(y>mid) ans+=ask(p*2+1,x,y);//累加答案,返回左右儿子的和 return ans; } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n); for(int i=1;i<=m;i++) { int q,x,y,z; scanf("%d",&q); if(q==1){ scanf("%d%d%d",&x,&y,&z); change(1,x,y,z); } else { scanf("%d%d",&x,&y); printf("%lld ",ask(1,x,y)); } } return 0; }
最大数
#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct tree{ int l,r; long long dat; }t[8000000]; int n,m;long long a[8000000]; int len,w; long long inf=0x7fffffff; void build(int p,int l,int r){ t[p].l=l,t[p].r=r; if(l==r){t[p].dat=a[l];return;} int mid=(l+r)/2; build(p*2,l,mid); build(p*2+1,mid+1,r); t[p].dat=max(t[p*2].dat,t[p*2+1].dat); } void change(int p,int x,int v){ if(t[p].l==t[p].r){t[p].dat=v;return;} int mid=(t[p].l+t[p].r)/2; if(x<=mid)change(p*2,x,v); else change(p*2+1,x,v); t[p].dat=max(t[p*2].dat,t[p*2+1].dat); } long long ask(int p,int l,int r){ if(l<=t[p].l&&r>=t[p].r)return t[p].dat; int mid=(t[p].l+t[p].r)/2; long long val=-inf; if(l<=mid)val=max(val,ask(p*2,l,r)); if(r>mid)val=max(val,ask(p*2+1,l,r)); return val; } int main(){ scanf("%d%d",&n,&m); memset(a,-inf,sizeof(a)); build(1,1,n); for(int i=1;i<=n;i++) { char s[2]; int q; scanf("%s",s); scanf("%d",&q); if(s[0]=='A'){ q%=m; w%=m; q=(q+w)%m; len++;change(1,len,q); } else { if(q==0){ printf("0"); continue; } w=ask(1,len-q+1,len);printf("%d ",w); } } return 0; }
线段树2 维护序列
#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct shu{ int l,r,dat; long long sum,add,mum; }t[10000000]; int a[1000000],n,m,mod; void build(int p,int l,int r){ t[p].l=l,t[p].r=r;t[p].mum=1; t[p].add=0; if(l==r){ t[p].sum=a[l]; return; } int mid=l+r>>1; build(p*2,l,mid); build(p*2+1,mid+1,r); t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod; } void spread(int p){ int mid=(t[p].r+t[p].l)/2; t[p*2].mum=(t[p*2].mum*t[p].mum)%mod; t[p*2+1].mum=(t[p*2+1].mum*t[p].mum)%mod; t[p*2].add=(t[p*2].add*t[p].mum)%mod; t[p*2+1].add=(t[p*2+1].add*t[p].mum)%mod; t[p*2].sum=(t[p*2].sum*t[p].mum)%mod; t[p*2+1].sum=(t[p*2+1].sum*t[p].mum)%mod; t[p].mum=1; //先乘再加 t[p*2].add=(t[p*2].add+t[p].add)%mod; t[p*2+1].add=(t[p*2+1].add+t[p].add)%mod; t[p*2].sum=(t[p*2].sum+(mid-t[p].l+1)*t[p].add)%mod; t[p*2+1].sum=(t[p*2+1].sum+(t[p].r-mid)*t[p].add)%mod; t[p].add=0; } /*1.子树的sum、mulv、addv值分别乘上当前节点的mulv值; 2.当前节点的mulv值还原,即置为1; 3.子树的addv值加上当前节点的addv值; 4.子树的sum值加上(子树包含元素数量*当前节点的addv值); 5.当前节点的addv值还原,即置为0。*/ void changesum(int p,int x,int y,int z){ if(x<=t[p].l && y>=t[p].r){//被覆盖的话,就对其进行修改 t[p].sum=(t[p].sum+z*(t[p].r-t[p].l+1))%mod; t[p].add=(t[p].add+z)%mod;//打上懒标记 return; } //if(t[p].mum!=1||t[p].add) int mid=(t[p].l+t[p].r)>>1; spread(p); if(x<=mid) changesum(p*2,x,y,z);//如果要修改的区间覆盖了左儿子,就修改左儿子 if(y>mid) changesum(p*2+1,x,y,z);//右儿子同理 t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;//最终维护的值等于左儿子的值+右儿子的值 } void changemum(int p,int x,int y,int z){ if(x<=t[p].l && y>=t[p].r){//被覆盖的话,就对其进行修改 t[p].sum=(t[p].sum*z)%mod; t[p].add=(t[p].add*z)%mod;//打上懒标记 t[p].mum=(t[p].mum*z)%mod; return; } if(t[p].mum!=1||t[p].add)spread(p); int mid=t[p].l+t[p].r>>1; if(x<=mid) changemum(p*2,x,y,z);//如果要修改的区间覆盖了左儿子,就修改左儿子 if(y>mid) changemum(p*2+1,x,y,z);//右儿子同理 t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;//最终维护的值等于左儿子的值+右儿子的值 } long long ask(int p,int x,int y){ if(x<=t[p].l && y>=t[p].r) return t[p].sum%mod;//如果被覆盖,就返回维护的值 int mid=(t[p].l+t[p].r)>>1; //if(t[p].mum!=1||t[p].add) spread(p);//下传懒标记,并查询左右儿子 long long ans=0; if(x<=mid) ans+=ask(p*2,x,y); if(y>mid) ans+=ask(p*2+1,x,y);//累加答案,返回左右儿子的和 return ans%mod; } int main(){ scanf("%d%d%d",&n,&m,&mod); for(register int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n); for(register int i=1;i<=m;i++){ int a1,a2,a3,a4; scanf("%d",&a1); if(a1==1){ scanf("%d%d%d",&a2,&a3,&a4); changemum(1,a2,a3,a4); } if(a1==2){ scanf("%d%d%d",&a2,&a3,&a4); changesum(1,a2,a3,a4); } if(a1==3){ scanf("%d%d",&a2,&a3); printf("%lld ",ask(1,a2,a3)); } } return 0; }
花神周游列国 开平方暴力修改
#include<iostream> #include<cstdio> #include<cmath> using namespace std; struct tree{ int l,r; long long dat,mmax; }t[1000000]; int n,m; long long a[1000000]; void build(int p,int l,int r){ t[p].l=l,t[p].r=r; if(l==r){t[p].dat=t[p].mmax=a[l];return;} int mid=(l+r)/2; build(p*2,l,mid); build(p*2+1,mid+1,r); t[p].dat=t[p*2].dat+t[p*2+1].dat; t[p].mmax=max(t[p*2].mmax,t[p*2+1].mmax); } void change(int p,int l,int r){ if(t[p].l==t[p].r){ t[p].dat=sqrt(t[p].dat); t[p].mmax=t[p].dat; return; } int mid=(t[p].l+t[p].r)/2; if(l<=mid&&t[p*2].mmax>1)change(p*2,l,r); if(r>mid&&t[p*2+1].mmax>1)change(p*2+1,l,r); t[p].dat=t[p*2].dat+t[p*2+1].dat; t[p].mmax=max(t[p*2].mmax,t[p*2+1].mmax); } long long ask(int p,int l,int r){ if(l<=t[p].l&&r>=t[p].r)return t[p].dat; int mid=(t[p].l+t[p].r)/2; long long val=0; if(l<=mid)val+=ask(p*2,l,r); if(r>mid)val+=ask(p*2+1,l,r); return val; }
#include<cstdio> #include<cstring> #include<iostream> #include<cmath> #define ll long long using namespace std; struct tree{ int l,r; ll add,sum; }t[1000000]; int n,c,m; void build(int p,int l,int r){ t[p].l=l; t[p].r=r; t[p].add=0;t[p].sum=1; if(l==r){ return; } int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); t[p].sum=t[p*2].sum|t[p*2+1].sum; } /*int count(int x){ int sum=0; while(x){ sum+=x%2; x/=2; } return sum; } x%2等价于x&1 x/=2==x>>1; */ /*int go(int x){ int sum=1; x--; while(x) sum*=2,x--; return sum; } sum*=2,x--==1<<sum */ void spread(int p){ t[p<<1|1].sum=t[p<<1].sum=t[p].add; t[p<<1|1].add=t[p<<1].add=t[p].add; t[p].add=0; } void change(int p,int x,int y,int k){ if(x<=t[p].l&&t[p].r<=y){ t[p].sum=k; t[p].add=k; return; } if(t[p].add)spread(p); int mid=(t[p].l+t[p].r)>>1; if(mid>=x)change(p<<1,x,y,k); if(mid<y)change(p<<1|1,x,y,k); t[p].sum=t[p<<1].sum|t[p<<1|1].sum; }//乘法标记是*=,加法标记是+=,赋值标记是= ll ask(int p,int x,int y){ if(x<=t[p].l&&t[p].r<=y)return t[p].sum; if(t[p].add)spread(p); int mid=(t[p].l+t[p].r)>>1; long long ans=0; if(mid>=x)ans=ans|ask(p<<1,x,y); if(mid<y)ans=ans|ask(p<<1|1,x,y); return ans; } int main(){ scanf("%d%d%d",&n,&c,&m); build(1,1,n); while(m--){ char s[2]; int a1,a2,a3; scanf("%s",s); if(s[0]=='C'){ scanf("%d%d%d",&a1,&a2,&a3); if(a1>a2)swap(a1,a2); change(1,a1,a2,1<<a3-1); } else { scanf("%d%d",&a1,&a2); if(a1>a2)swap(a1,a2); ll num=ask(1,a1,a2); ll ans=0; for(int i=0;i<=c;i++)if(num&(1<<i))ans++;//表示若序列中有这种颜色 //注意i=0,因为要考虑=1情况 printf("%lld ",ans); } } return 0; }
int main(){ scanf("%d",&n); for(register int i=1;i<=n;i++) scanf("%lld",&a[i]); build(1,1,n); scanf("%d",&m); for(register int i=1;i<=m;i++){ int a1,a2,a3; scanf("%d%d%d",&a1,&a2,&a3); if(a2>a3)swap(a2,a3); if(!a1)change(1,a2,a3); else printf("%lld ",ask(1,a2,a3)); } return 0; }
色板游戏