191104-线段树练习

191104-线段树(树状数组)练习

T1 序列计数

解析

首先正着扫一遍,给比每个数后面大的数加1,然后反着扫一遍最后统计每个点两遍扫出来的值相乘,累加起来即可(树状数组维护)

题解

#include<bits/stdc++.h>//树状数组
#define int long long
using namespace std;
int tree[100009],a[100009],b[100009],ans,l[100009],r[100009],n,len;
int lowbit(int i){return i&(-i);}
void update(int x){
	while(x<=len){
		tree[x]+=1;
		x+=lowbit(x);
	}
}
int solve(int x){
	int sum=0;
	while(x>0){
		sum+=tree[x];
		x-=lowbit(x);
	}
	return sum;
}
signed main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+n+1);
	len=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(b+1,b+len+1,a[i])-b;
		l[i]=solve(a[i]);
		update(a[i]+1);
	}
	memset(tree,0,sizeof(tree));
	for(int i=n;i>=1;i--){
		r[i]=solve(a[i]);
		update(a[i]+1);
		ans+=(r[i]*l[i]);
	}
	printf("%lld",ans);
	return 0;	
}

T2 序列操作3

区间加,区间乘,区间求和
特别注意的是在pushdown的时候应该先乘后加,因为add值也已经是乘过后的值了,同时该题也要注意取模

题解

#include<bits/stdc++.h>
#define M 100006
#define int long long
#define il inline
#define ree register
#pragma GCC optimize(3)
using namespace std;
struct zb
{
	int l,r,add,sum,mul;
}tr[M*4];
il void Write(int x){
	if(x<0){
		x=-x;
	}
	if(x>9)  Write(x/10);
	putchar(x%10+'0');
}
il int read()
{
	int f=1,re=0;
	char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-')
	{
		f=-1;
		ch=getchar();
	}
	for(;isdigit(ch);ch=getchar())
		re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
const int mod=1e9+7;
int a[M],n,m;
il void build(int k,int l,int r)
{
	tr[k].l=l;
	tr[k].r=r;
	tr[k].mul=1;
	if(l==r)
	{
		tr[k].sum=a[l]%mod;
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
	tr[k].sum%=mod;
	if(tr[k].sum<0) tr[k].sum+=mod;
}
il void add_1(int k,int val)
{
	tr[k].add+=val;
	tr[k].add%=mod;
	tr[k].sum=(tr[k].sum+val*(tr[k].r-tr[k].l+1))%mod;
	if(tr[k].add<0) tr[k].sum+=mod;
	if(tr[k].sum<0) tr[k].mul+=mod;
}
il void add_2(int k,int val)
{
	tr[k].mul*=val;
	tr[k].mul%=mod;
	tr[k].sum=(val*tr[k].sum)%mod;
	tr[k].add*=val;
	tr[k].add%=mod;
	if(tr[k].add<0) tr[k].add+=mod;
	if(tr[k].sum<0) tr[k].sum+=mod;
	if(tr[k].mul<0) tr[k].mul+=mod;
}
il void pushdown(int k)
{
	add_2(k<<1,tr[k].mul);
	add_2(k<<1|1,tr[k].mul);
	tr[k].mul=1;
	add_1(k<<1,tr[k].add);
	add_1(k<<1|1,tr[k].add);
	tr[k].add=0;
}
il void update_1(int k,int l,int r,int val)
{
	if(tr[k].l>=l&&tr[k].r<=r) return add_1(k,val);
	pushdown(k);
	int mid=(tr[k].l+tr[k].r)>>1;
	if(l<=mid) update_1(k<<1,l,r,val);
	if(r>mid) update_1(k<<1|1,l,r,val);
	tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
	tr[k].sum%=mod;
	if(tr[k].sum<0) tr[k].sum+=mod;
}
il void update_2(int k,int l,int r,int val)
{
	if(tr[k].l>=l&&tr[k].r<=r) return add_2(k,val);
	pushdown(k);
	int mid=(tr[k].l+tr[k].r)>>1;
	if(l<=mid) update_2(k<<1,l,r,val);
	if(r>mid) update_2(k<<1|1,l,r,val);
	tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
	tr[k].sum%=mod;
	if(tr[k].sum<0) tr[k].sum+=mod;
}
il int solve(int k,int l,int r)
{
	if(tr[k].l>=l&&tr[k].r<=r) return tr[k].sum;
	pushdown(k);
	int mid=(tr[k].l+tr[k].r)>>1;
	int ret=0;
	if(l<=mid) ret=(ret+solve(k<<1,l,r))%mod;
	if(r>mid) ret=(ret+solve(k<<1|1,l,r))%mod;
	return ret%mod;
}
signed main()
{
	int l,r,opt,x;
	n=read();m=read();
	for(ree int i=1;i<=n;i++)
		a[i]=read();
	build(1,1,n);
	for(ree int i=1;i<=m;i++)
	{
		opt=read();l=read();r=read();
		if(opt==3) Write(solve(1,l,r)%mod),putchar('\n');
		if(opt==0)
		{
			x=read();
			update_1(1,l,r,x);
		}
		if(opt==1)
		{
			x=read();
			update_2(1,l,r,x);
		}
		if(opt==2)
		{
			x=read();
			update_2(1,l,r,-1);
			update_1(1,l,r,x);
		}
	}
}

T3 SP1043

解析

求区间内最大连续子段和
考虑多维护几个值
1,lmax:区间内从最左端开始的最大连续子段和
2,rmax:区间内从最右端开始的最大连续子段和
3,sum:区间和
4,dat:区间内最大子段和
因此就有:
t r [ k ] . s u m = t r [ k < < 1 ] . s u m + t r [ k < < 1 ∣ 1 ] . s u m t r [ k ] . l m a x = m a x ( t r [ k < < 1 ] . l m a x , t r [ k < < 1 ] . s u m + t r [ k < < 1 ∣ 1 ] . l m a x ) t r [ k ] . r m a x = m a x ( t r [ k < < 1 ∣ 1 ] . r m a x , t r [ k < < 1 ∣ 1 ] . s u m + t r [ k < < 1 ] . r m a x ) t r [ k ] . d a t = m a x ( t r [ k < < 1 ] . d a t , m a x ( t r [ k < < 1 ∣ 1 ] . d a t , t r [ k < < 1 ] . r m a x + t r [ k < < 1 ∣ 1 ] . l m a x ) ) tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum tr[k].lmax=max(tr[k<<1].lmax,tr[k<<1].sum+tr[k<<1|1].lmax) tr[k].rmax=max(tr[k<<1|1].rmax,tr[k<<1|1].sum+tr[k<<1].rmax) tr[k].dat=max(tr[k<<1].dat,max(tr[k<<1|1].dat,tr[k<<1].rmax+tr[k<<1|1].lmax)) tr[k].sum=tr[k<<1].sum+tr[k<<11].sumtr[k].lmax=max(tr[k<<1].lmax,tr[k<<1].sum+tr[k<<11].lmax)tr[k].rmax=max(tr[k<<11].rmax,tr[k<<11].sum+tr[k<<1].rmax)tr[k].dat=max(tr[k<<1].dat,max(tr[k<<11].dat,tr[k<<1].rmax+tr[k<<11].lmax))
(询问部分还需好好理解)

题解

#include<bits/stdc++.h>
#define M 200006
using namespace std;
int n,m,a[M];
struct qrx
{
	int l,r,lmax,rmax,dat,sum;
}tr[M*4];
void pushup(int k)
{
	tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
	tr[k].lmax=max(tr[k<<1].lmax,tr[k<<1].sum+tr[k<<1|1].lmax);
	tr[k].rmax=max(tr[k<<1|1].rmax,tr[k<<1|1].sum+tr[k<<1].rmax);
	tr[k].dat=max(tr[k<<1].dat,max(tr[k<<1|1].dat,tr[k<<1].rmax+tr[k<<1|1].lmax));
}
void build(int k,int l,int r)
{
	tr[k].l=l;
	tr[k].r=r;
	if(l==r)
	{
		tr[k].lmax=tr[k].rmax=tr[k].sum=tr[k].dat=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}
qrx solve(int k,int l,int r)
{
	if(tr[k].l>=l&&tr[k].r<=r) return tr[k];
	int mid=(tr[k].l+tr[k].r)>>1;
	if(r<=mid) return solve(k<<1,l,r);
	if(l>mid) return solve(k<<1|1,l,r);
	else
	{
		qrx d=solve(k<<1|1,l,r);
		qrx c=solve(k<<1,l,r);
		qrx ret;
		ret.sum=c.sum+d.sum;
		ret.lmax=max(c.lmax,c.sum+d.lmax);
		ret.rmax=max(c.rmax+d.sum,d.rmax);
		ret.dat=max(max(d.dat,c.dat),d.lmax+c.rmax);
		return ret;
	}
}
int main()
{
	int l,r;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	build(1,1,n);
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&l,&r);
		printf("%d\n",solve(1,l,r).dat);
	}
	return 0;
}

T3 SP1716

与上题相似,只是再加上一个单点修改即可

题解

#include<bits/stdc++.h>
#define M 200006
using namespace std;
int n,m,a[M];
struct qrx
{
	int l,r,lmax,rmax,dat,sum;
}tr[M*4];
void pushup(int k)
{
	tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
	tr[k].lmax=max(tr[k<<1].lmax,tr[k<<1].sum+tr[k<<1|1].lmax);
	tr[k].rmax=max(tr[k<<1|1].rmax,tr[k<<1|1].sum+tr[k<<1].rmax);
	tr[k].dat=max(tr[k<<1].dat,max(tr[k<<1|1].dat,tr[k<<1].rmax+tr[k<<1|1].lmax));
}
void build(int k,int l,int r)
{
	tr[k].l=l;
	tr[k].r=r;
	if(l==r)
	{
		tr[k].lmax=tr[k].rmax=tr[k].sum=tr[k].dat=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}
void update(int k,int pos,int val)
{
	if(tr[k].l==tr[k].r)
	{
		tr[k].lmax=tr[k].rmax=tr[k].sum=tr[k].dat=val;
		return;
	}
	int mid=(tr[k].l+tr[k].r)>>1;
	if(pos<=mid) update(k<<1,pos,val);
	else update(k<<1|1,pos,val);
	pushup(k);
}
qrx solve(int k,int l,int r)
{
	if(tr[k].l>=l&&tr[k].r<=r) return tr[k];
	int mid=(tr[k].l+tr[k].r)>>1;
	if(r<=mid) return solve(k<<1,l,r);
	if(l>mid) return solve(k<<1|1,l,r);
	else
	{
		qrx d=solve(k<<1|1,l,r);
		qrx c=solve(k<<1,l,r);
		qrx ret;
		ret.sum=c.sum+d.sum;
		ret.lmax=max(c.lmax,c.sum+d.lmax);
		ret.rmax=max(c.rmax+d.sum,d.rmax);
		ret.dat=max(max(d.dat,c.dat),d.lmax+c.rmax);
		return ret;
	}
}
int main()
{
	int opt,l,r;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	build(1,1,n);
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&opt,&l,&r);
		if(opt==1) 
		{
			if(l>r) swap(l,r);
			printf("%d\n",solve(1,l,r).dat);
		}
		else update(1,l,r);
	}
	return 0;
}

T4 SP2713

解析

区间开方,区间求和
注意到1e18的数 开六次方就几近为1
因此,我们维护每个区间的最大值,当该区间的最大值都已经等于1了,那么该区间也就不需要再进行开方操作了,直接跳过就好了,这样会大大节省时间复杂度

题解

#include<bits/stdc++.h>//树状数组-会t 
using namespace std;
long long tree[100005],n,cnt,opt,l,r,m;
long long a[100005];
long long read()
{
	int f=1;
	long long re=0;
	char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-')
	{
		f=-1;
		ch=getchar();
	}
	for(;isdigit(ch);ch=getchar())
		re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
int lowbit(int i)
{
	return i&(-i);
}
void update(int x,int y)
{
	while(x<=n)
	{
		tree[x]+=y;
		x+=lowbit(x);
	}
}
int solve(int x)
{
	int sum=0;
	while(x>0)
	{
		sum+=tree[x];
		x-=lowbit(x);
	}
	return sum;
}
int main()
{
	while(scanf("%lld",&n)!=EOF)
	{
		cnt++;
		printf("Case #%lld:\n",cnt);
		memset(tree,0,sizeof(tree));
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;i++)
			a[i]=read();
		m=read();
		for(int i=1;i<=m;i++)
		{
			opt=read();
			l=read();
			r=read();
			if(opt)
			{
				long long ans=0;
				for(int j=l;j<=r;j++)
				{
					if(a[j]==1) 
					{
						ans++;
						continue;
					}
					int k=solve(j);
					for(int e=1;e<=k&&a[j]!=1;e++)
						a[j]=sqrt(a[j]);
					ans+=a[j];
				}
				printf("%lld\n",ans);
			}
			else
			{
				update(l,1);
				update(r+1,-1);
			}
		}
		printf("\n");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值