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<<1∣1].sumtr[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))
(询问部分还需好好理解)
题解
#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;
}