线段树区间操作

维护区间gcd

给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:

1、“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。//区间修改

2、“Q l r”,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。

对于每个询问,输出一个整数表示答案。

Solution

  1. gcd(a,b)=gcd(a,b−a)
    更相减损术,可以推广到多个数的情况。
    更相减损术其实是欧几里得算法的一个特例。即gcd(a−nb,b)=gcd(a,b)。
    (a,b,c)=((a,b),(b,c))=((a,b−a),(b,c−b))=(a,b−a,b,c−b)
    由于(b−a,b)=(a,b−a)所以(a,b,c)=(a,b−a,c−b)
    有了这个式子说明可以通过维护序列的差分来达到求gcd同样的效果。
    然后差分就可以把区间加减变成单点加减。可以用没有lazy的线段树来做。
    再维护一个差分,做成树状数组或者线段树,用来维护每个数的值。

  2. gcd(a,b)=gcd(a,−b)
    在数值加减的过程中可能会产生负数,而约定gcd是没有负数的,所以需要用这个式子来搞定负数。
    具体来说,就是在每次查询或者更新的时候,如果遇到了负数,就把它取反进行运算。
    注意只能进行运算而不能直接把线段树的叶子节点取反。因为直接把叶子取反会对今后的加减操作造成影响。
    虽然gcd(a,b)=gcd(a,−b)gcd(a,b)=gcd(a,−b)但是(a+1,b)和(a+1,−b)不一定相等。

差分操作是a[x]+d,a[y+1]-d;这个时候就可能出现y+1越界的情况。需要及时特判掉。

由于gcd 的性质,易证不会超过logn???

友情提示long long 莫忘

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
#define ls p<<1
#define rs p<<1|1
#define N 500500
struct node{
	ll l,r,dat;
}t[N*4];
ll a[N],c[N],b[N];
ll n,m;
inline ll gcd(ll a,ll b){
	return b==0?a:gcd(b,a%b);
}
inline ll jue(ll x){
	if(x<0)return -x;
	else return x;
}
void build(int p,ll l,ll r){
	t[p].l=l;t[p].r=r;
	if(l==r){
		t[p].dat=c[l];return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	t[p].dat=gcd(t[ls].dat,t[rs].dat);
}
void update(int p,ll x,ll v){
	if(x>n)return;
	if(t[p].l==t[p].r){
		t[p].dat+=v;return;	
	}
	int mid=(t[p].l+t[p].r)>>1;
	if(x<=mid)update(ls,x,v);
	else update(rs,x,v);
	t[p].dat=gcd(t[ls].dat,t[rs].dat);
}
ll query(int p,ll l,ll r){
	if(l>r)return 0;
	if(l<=t[p].l&&t[p].r<=r)return jue(t[p].dat);
	int mid=(t[p].l+t[p].r)>>1;
	ll f1=0,f2=0;
	if(l<=mid)f1=query(ls,l,r);
	if(mid<r)f2=query(rs,l,r);
	return jue(gcd(f1,f2));
}
ll add(ll x,ll v,ll *b){
	for(;x<=n;x+=x&(-x))b[x]+=v;
}
ll ask(ll x,ll *b){
	ll res=0;
	for(;x;x-=x&(-x))res+=b[x];
	return res;
}
int main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		c[i]=a[i]-a[i-1];
	}
	build(1,1,n);
	char ch[5];
	ll xx,yy,v;
	while(m--){
		scanf("%s",ch);
		if(ch[0]=='Q'){
			scanf("%lld%lld",&xx,&yy);
			printf("%lld\n",gcd(a[xx]+ask(xx,b),query(1,xx+1,yy)));
		}else{
			scanf("%lld%lld%lld",&xx,&yy,&v);
            update(1,xx,v);
            update(1,yy + 1,-v);
            add(xx,v,b);add(yy+1,-v,b);
		}
	}
	return 0;
}

区间取模

和下面的类似

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005
#define inf 0x3f3f3f3f
#define ls p<<1
#define rs p<<1|1
inline int read(){
	int res=0;bool f=1;char c;c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){res=res*10+c-'0';c=getchar();}
	return f*res;
}
int minn(int x,int y){
	return x>y?y:x;
}
struct tree{
	int l,r,w;
}t[4*N];
int val[4*N],n,m,x,y,z;
void build(int l,int r,int p){
	t[p].l=l;
	t[p].r=r;
	if(t[p].l==t[p].r){
		t[p].w=val[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,ls);
	build(mid+1,r,rs);
	t[p].w=minn(t[ls].w,t[rs].w);
}
void query(int p,int ql,int qr){
	if(t[p].l==t[p].r){
		x%=t[p].w;
		return;
	}
	int mid=(t[p].l+t[p].r)>>1;
	if(ql<=mid&&x>=t[ls].w)query(ls,ql,qr);
	if(qr>mid&&x>=t[rs].w)query(rs,ql,qr);
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)
		val[i]=read();
	build(1,n,1);
	for(int i=1;i<=m;i++){
		x=read();y=read();z=read();
		query(1,y,z);
		printf("%d\n",x);
	}
	return 0;
}

区间开根号

sqrt(1)=1,只有最大值大于1时才有修改的必要,维护最大值和sum即可

!!!血与泪的教训。。。mx[]忘开根号。。。。T飞了

啊啊啊啊啊啊

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL; 
#define ls (p<<1)
#define rs (p<<1|1)
const int N=500005;
inline LL read(){
	LL x=0;char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x;
}
inline int read1(){
	int x=0;char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x;
}
int n,m;
LL sum[N],mx[N],a[N];
LL max(LL a,LL b){
	return a>b?a:b;
}
void build(int l,int r,int p){
	if(l==r){
		sum[p]=mx[p]=a[l];
		return;
	} 
	int mid=(l+r)>>1;
	build(l,mid,ls);
	build(mid+1,r,rs);
	sum[p]=sum[ls]+sum[rs];
	mx[p]=max(mx[ls],mx[rs]);
}
void modify(int l,int r,int L,int R,int p){
	if(mx[p]<=1) return;
	if(l==r){
		sum[p]=(LL)sqrt(sum[p]);
        mx[p]=(LL)sqrt(mx[p]);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid) modify(l,mid,L,R,ls);
	if(R>mid) modify(mid+1,r,L,R,rs);
	sum[p]=sum[ls]+sum[rs];
	mx[p]=max(mx[ls],mx[rs]);	
}
LL query(int l,int r,int L,int R,int p){
	if(L<=l&&r<=R) return sum[p];
	int mid=(l+r)>>1;
	LL res=0;
	if(L<=mid) res+=query(l,mid,L,R,ls);
	if(R>mid) res+=query(mid+1,r,L,R,rs);
	return res;
} 
int main(){
	n=read1();
	for(int i=1;i<=n;i++) a[i]=read();
	build(1,n,1);
	m=read1();
	for(int i=1,op,x,y;i<=m;i++){
		op=read1();x=read1();y=read1();
		if(op==0){
			if(x>y)swap(x,y);
			modify(1,n,x,y,1);
		}else{
			if(x>y)swap(x,y);
			printf("%lld\n",query(1,n,x,y,1));
		}
	}
	return 0;	
}

区间修改为正约数个数

预处理,单点暴力修改

预处理一个d[ ]
 for(int i=1;i<=1000000;i++)
		for(int j=1;i*j<=1000000;j++)
			d[i*j】++;
注意到, if 区间a[i]最大值 <=2,则不用修改 
else 暴力修改 
由于d[x]下降很快,几次后便降到<=2,所以复杂度优秀(大约是nlogn?)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls p<<1
#define rs p<<1|1
const int N=330000;
ll d[1000005]={0};
int n,m; 
ll a[N];
ll t[N*4],mx[N*4];
inline void pushup(int p){
	t[p]=t[ls]+t[rs];
	mx[p]=max(mx[ls],mx[rs]);
}
inline void build(int l,int r,int p){
	if(l==r){
		mx[p]=a[l];t[p]=a[l];return;
	}
	int mid=(l+r)>>1;
	build(l,mid,ls);
	build(mid+1,r,rs);
	pushup(p);
}
inline void update(int l,int r,int p,int ql,int qr){
	if(l>qr||r<ql)return;
	if(mx[p]<=2)return;
	if(ql<=l&&r<=qr&&l==r){
		t[p]=d[t[p]];mx[p]=t[p];return;
	}
	int mid=(l+r)>>1;
	update(l,mid,ls,ql,qr);
	update(mid+1,r,rs,ql,qr);
	pushup(p);
}
ll query(int l,int r,int p,int ql,int qr){
	if(l>qr||r<ql)return 0;
	if(l>=ql&&r<=qr)return t[p];
	int mid=(l+r)>>1;
	return query(l,mid,ls,ql,qr)+query(mid+1,r,rs,ql,qr);
} 
int main(){
	for(int i=1;i<=1000000;i++)
		for(int j=1;i*j<=1000000;j++)
			d[i*j]++;
	scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    build(1,n,1); 
    while(m--){
    	int opt=0,x,y;
        scanf("%d%d%d",&opt,&x,&y);
        if(opt==1) update(1,n,1,x,y);
        else printf("%lld\n",query(1,n,1,x,y));
	}
	return 0;
} 

01串+翻转

维护一个01串,一开始全部都是0
3种操作
1.把一个区间都变为1
2.把一个区间都变为0
3.把一个区间的所有数字翻转过来
每次操作完成之后询问区间最小的0的位置
l,r<= $ 10^{18} $ __离散化

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 400005
#define ls p<<1
#define rs p<<1|1
using namespace std;
typedef long long ll;
int n,len,opt[N];
int atag[N],xtag[N];
ll ql[N],qr[N],b[N];
struct tree{
	int sum,rev,tag;
}t[N<<2];
inline void pushup(int p){
	t[p].sum=t[ls].sum+t[rs].sum;
}
void adjust1(int p,int l,int r,int v){
	t[p].rev=0;
	t[p].tag=v--;
	t[p].sum=v*(r-l+1);
}
void adjust2(int p,int l,int r){
	t[p].sum=r-l+1-t[p].sum;
	t[p].rev^=1;
}
void pushdown(int p,int l,int r){
	int mid=(l+r)>>1;
	if(t[p].tag){
		adjust1(ls,l,mid,t[p].tag);
		adjust1(rs,mid+1,r,t[p].tag);
		t[p].tag=0;
	}
	if(t[p].rev){
		adjust2(ls,l,mid);
		adjust2(rs,mid+1,r);
		t[p].rev=0;
	}
}
void modify(int p,int l,int r,int L,int R,int v){
	if(L<=l&&r<=R){
		adjust1(p,l,r,v);return;
	}
	pushdown(p,l,r);
	int mid=(l+r)>>1;
	if(L<=mid) modify(ls,l,mid,L,R,v);
	if(R>mid) modify(rs,mid+1,r,L,R,v);
	pushup(p);
}
void rever(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R){
		adjust2(p,l,r);return;
	}
	pushdown(p,l,r);
	int mid=(l+r)>>1;
	if(L<=mid) rever(ls,l,mid,L,R);
	if(R>mid) rever(rs,mid+1,r,L,R);
	pushup(p);
}
int query(int p,int l,int r){
	if(l==r)return l;
	pushdown(p,l,r);
	int mid=(l+r)>>1,ans;
	if(t[ls].sum!=mid-l+1) ans=query(ls,l,mid);
	else ans=query(rs,mid+1,r);
	pushup(p);
	return ans;
}

int main(){
	scanf("%d",&n);b[++len]=1;
	for(int i=1;i<=n;i++){
		scanf("%d%lld%lld",&opt[i],&ql[i],&qr[i]);
		b[++len]=ql[i];b[++len]=qr[i];
		b[++len]=ql[i]+1;b[++len]=qr[i]+1;
	}
	sort(b+1,b+1+len);len=unique(b+1,b+1+len)-b-1;//排序去重 
	for(int i=1;i<=n;i++){
		ql[i]=lower_bound(b+1,b+1+len,ql[i])-b;//离散化 
		qr[i]=lower_bound(b+1,b+1+len,qr[i])-b;
		if(opt[i]==1) modify(1,1,len,ql[i],qr[i],2);
		else if(opt[i]==2) modify(1,1,len,ql[i],qr[i],1);
		else rever(1,1,len,ql[i],qr[i]);
		printf("%lld\n",b[query(1,1,len)]);
	}
	return 0;
}
posted @ 2020-08-13 23:59  ke_xin  阅读(68)  评论(0编辑  收藏  举报