一.动态连续和查询问题
问题描述
给定一个n个元素的数组a1,a2,a3…..an,你的任务是设计一个数据结构支持以下两种操作:
(1)Add(x,d): 让a[x]增加d
(2)Query(L,R): 计算L到R的区间和
输入文件
输入的第 1 行包含一个整数 n 表示序列长度。
接下来一行包含n个整数,分别是A[1], A[2], …, A[n]。
接下来一行包含一个整数m,表示询问个数。
接下来每行一个询问:
0 x y : 将 A[x]增加y
1 x y : 询问x到y的区间和
输出文件
对于每个询问1 x y,输出一行为要求的答案。
输出样例
4
1 2 3 4
4
1 1 3
0 3 -3
1 2 4
1 3 3
输出样例
6
6
0
#include
#include
#include
using namespace std;
int n,m;
int q,l,r;
int f[1000005];
int f1[1000006];
int k;
inline int lowbit(int x)
{
return x&(-x);
}
inline int get(int x)
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
{
sum+=f[i];
}
return sum;
}
void add1(int x,int d)
{
for(int i=x;i<=n;i=i+lowbit(i)){f[i]=f[i]+d;}
return;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&q);
add1(i,q);
}
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d%d",&k,&x,&y);
if (k==0) add1(x,y);
if (k==1) printf("%d\n",get(y)-get(x-1));
}
return 0;
}
二.数列操作1
问题描述
给定一个初始值都为0的序列,动态地修改一些位置上的数字,加上一个数,减去一个数,或者乘上一个数,然后动态地提出问题,问题的形式是求出一段数字的和。
输入文件
输入数据第一行包含2个整数N,M(1≤N,M≤100000),表示序列的长度和操作的次数。
接下来M行,每行会出现如下的一种格式:
1 i x ——将序列中第i个数加上x
2 i x ——将序列中第i个数减去x
3 i x ——将序列中第i个数乘上x
4 i j ——求出序列中第i个数到第j个数的和
输出文件
对于每一个Query操作输出一个数,表示序列中第i个数到第j个数的和。
样例输入
10
5
1 3 2
2 4 1
3 3 2
4 1 3
4 2 4
样例输出
4
3
#include
#include
#include
#include
using namespace std;
int n,m;
int c[100005];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline int lowbit(int x){return x&(-x);}
inline void add(int x,int y){
for(int i=x;i<=n;i+=lowbit(i)) c[i]+=y;
}
inline int gett(int x){
int anss=0;
for(int i=x;i;i-=lowbit(i)) anss+=c[i];
return anss;
}
int main(){
n=read();m=read();
for(int i=1;i<=m;i++){
int ord,x,y;
ord=read();x=read();y=read();
if(ord==1){
add(x,y);
}
if(ord==2){
add(x,-1*y);
}
if(ord==3){
int bb=gett(x)-gett(x-1);
add(x,(y-1)*bb);
}
if(ord==4){
printf("%d\n",gett(y)-gett(x-1));
}
}
return 0;
}
三.数列操作二
问题描述
给定一个初始值都为0的序列,动态地修改一个区间,加上一个数,减去一个数,,然后动态地提出问题,问题的形式是求出某一点的值。
输入文件
输入数据第一行包含2个整数N,M(1≤N,M≤1000000),表示序列的长度和操作的次数。
接下来M行,每行会出现如下的一种格式:
1 i j x——将第i个数到第j个数加上x
2 i j x ——将第i个数到第j个数减去x
3 i ——查询i点的值。
输出文件
对于每一个Query操作输出一个数。
样例输入
10
5
1 2 3 4
2 1 4 5
1 3 7 5
3 3
3 7
样例输出
4
5
#include
#include
#include
#include
using namespace std;
int n,m;
int a[100005];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct NODE{
int l,r;
long long sum,maxx,lazy,minn,b;
NODE *ls,*rs;
NODE():ls(NULL),rs(NULL),lazy(0){}
inline void maintain(){
sum=ls->sum+rs->sum;
maxx=max(ls->maxx,rs->maxx);
minn=min(ls->minn,rs->minn);
}
inline void pushdown(int l,int r){
int mid=(l+r)>>1;
if(lazy){
ls->maxx+=lazy;
ls->minn+=lazy;
rs->minn+=lazy;
ls->maxx+=lazy;
ls->lazy+=lazy;
rs->lazy+=lazy;
ls->sum+=(mid-l+1)*lazy;
rs->sum+=(r-(mid+1)+1)*lazy;
lazy=0;
}
}
}*root;
inline void maketree(int L,int R,NODE *&k){
k=new NODE;
if(L==R){
k->minn=k->maxx=k->sum=a[L];
return ;
}
int mid=(L+R)>>1;
maketree(L,mid,k->ls);maketree(mid+1,R,k->rs);
k->maintain();
}
inline void Add(int L,int R,int x,int y,int v,NODE *k){
if(L>=x && R<=y){
k->lazy+=v;
k->minn+=v;
k->maxx+=v;
k->sum+=(R-L+1)*v;
return ;
}
k->pushdown(L,R);
int mid=(L+R)>>1;
if(y<=mid) Add(L,mid,x,y,v,k->ls);
else if(x>mid) Add(mid+1,R,x,y,v,k->rs);
else Add(L,mid,x,y,v,k->ls),Add(mid+1,R,x,y,v,k->rs);
k->maintain();
}
inline int Query(int L,int R,int x,int y,NODE *k){
if(L>=x&&R<=y) return k->sum;
int mid=(L+R)>>1,t;
k->pushdown(L,R);
if(y<=mid) t=Query(L,mid,x,y,k->ls);
else if(x>mid) t=Query(mid+1,R,x,y,k->rs);
else t=Query(L,mid,x,y,k->ls)+Query(mid+1,R,x,y,k->rs);
k->maintain();
return t;
}
int main(){
n=read();m=read();
maketree(1,n,root);
for(int i=1;i<=m;i++){
int ord,x,y,z;
ord=read();
x=read();
if(ord==1){
y=read();
z=read();
Add(1,n,x,y,z,root);
}
else if(ord==2){
y=read();
z=read();
Add(1,n,x,y,-1*z,root);
}
else{
printf("%d\n",Query(1,n,x,x,root));
}
}
return 0;
}
四.求和
问题描述
给定一个序列a1,a2,…..an,动态地修改一个区间,加上一个数,,然后动态询问一个区间的所有元素的和。
输入文件
输入数据第一行包含2个整数N,M(1≤N,M≤1000000),表示序列的长度和操作的次数。
接下一行n个数,表示a1,a2,…..an。
再接下来M行,每行会出现如下的一种格式:
1 i j x——将第i个数到第j个数加上x
2 i j ——输出第i个数到第j个数的和
输出文件
对于每一个Query操作输出一个数,表示序列中第i个数到第j个数的和。
样例输入
10
1 2 3 4 5 6 1 2 3 4
5
1 2 3 4
2 1 4
1 3 7 5
1 2 6 5
2 1 10
样例输出
18
89
#include
#include
#include
using namespace std;
int n,m;
int a[100005];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct NODE{
int sum,v;
NODE *ls,*rs;
NODE():ls(NULL),rs(NULL),v(0){}
inline void maintain(){
sum=ls->sum+rs->sum;
}
inline void pushdown(int L,int R){
int mid=(L+R)>>1;
if(v){
ls->v+=v;
rs->v+=v;
ls->sum+=(mid-L+1)*v;
rs->sum+=(R-(mid+1)+1)*v;
v=0;
}
}
}*root;
inline void maketree(int L,int R,NODE *&k){
k=new NODE ;
if(L==R){
k->sum=a[L];
return ;
}
int mid=(L+R)>>1;
maketree(L,mid,k->ls);
maketree(mid+1,R,k->rs);
k->maintain();
}
inline void add(int L,int R,int x,int y,int z,NODE *k){
if(L>=x&&R<=y){
k->v+=z;
k->sum+=(R-L+1)*z;
return ;
}
k->pushdown(L,R);
int mid=(L+R)>>1;
if(y<=mid) add(L,mid,x,y,z,k->ls);
else if(x>mid) add(mid+1,R,x,y,z,k->rs);
else add(L,mid,x,y,z,k->ls),add(mid+1,R,x,y,z,k->rs);
k->maintain();
}
inline int Query(int L,int R,int x,int y,NODE *k){
if(L>=x&&R<=y){
return k->sum;
}
k->pushdown(L,R);
k->maintain();
int mid=(L+R)>>1,t;
if(y<=mid) t=Query(L,mid,x,y,k->ls);
else if(x>mid) t=Query(mid+1,R,x,y,k->rs);
else t=Query(L,mid,x,y,k->ls)+Query(mid+1,R,x,y,k->rs);
return t;
}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
maketree(1,n,root);
m=read();
for(int i=1;i<=m;i++){
int ord,x,y,k;
ord=read();x=read();y=read();
if(ord==1){
k=read();
add(1,n,x,y,k,root);
}
else {
printf("%d\n",Query(1,n,x,y,root));
}
}
return 0;
}
五.
问题描述
给定数列a1, a2, .., an和若干询问(l, r),回答区间最大值。
输入文件
一个整数N表示数字的个数,接下来一行为N个数。第三行读入一个M,表示提问的次数,接下来M行,每行都有两个整数l,r(l<=r)。
输出文件
输出共M行,每行输出一个数,表示提问区间的最大值。
样例输入
6
34 1 8 123 3 2
4
1 2
1 5
3 4
2 3
样例输出
34
123
123
8
#include
#include
#include
using namespace std;
const int INF=2147483640;
int n,m;
int a[200005];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct NODE{
int maxn;
NODE *ls,*rs;
NODE():ls(NULL),rs(NULL),maxn(-INF){}
inline void maintain(){
maxn=max(ls->maxn,rs->maxn);
}
}*root;
inline void maketree(int l,int r,NODE *&k){
k=new NODE;
if(l==r){
k->maxn=a[l];
return ;
}
int mid=(l+r)>>1;
maketree(l,mid,k->ls);
maketree(mid+1,r,k->rs);
k->maintain();
}
inline int Query(int l,int r,int x,int y,NODE *k){
if(l>=x&&r<=y){
return k->maxn;
}
int mid=(l+r)>>1,t;
k->maintain();
if(y<=mid) t=Query(l,mid,x,y,k->ls);
else if(x>mid) t=Query(mid+1,r,x,y,k->rs);
else t=max(Query(l,mid,x,y,k->ls),Query(mid+1,r,x,y,k->rs));
return t;
}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
maketree(1,n,root);
m=read();
for(int i=1;i<=m;i++){
int l,r;
l=read();r=read();
printf("%d\n",Query(1,n,l,r,root));
}
return 0;
}
六.
问题描述
给定数列a1, a2, .., an和若干询问(l, r),回答区间最大连续和。 输入文件 一个整数N表示数字的个数,接下来一行为N个数。第三行读入一个M,表示提问的次数,接下来M行,每行都有两个整数l,r(l<=r)。
输出文件
输出共M行,每行输出一个数,表示提问区间的最大连续和。
样例输入
9
1 3 -3 8 1 9 -5 -2 4
2
1 5
6 9
样例输出
10
9
#include
#include
#include
using namespace std;
const int INF=2147483640;
int n,m;
int a[200005];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct NODE{
int sum,lsum,rsum,maxsum;
NODE *ls,*rs;
NODE():ls(NULL),rs(NULL),sum(0){}
inline void maintain(){
sum=ls->sum+rs->sum;
lsum=max(ls->lsum,ls->sum+rs->lsum);
rsum=max(rs->rsum,rs->sum+ls->rsum);
maxsum=max(ls->rsum+rs->lsum,max(rs->maxsum,ls->maxsum));
}
}*root;
inline void maketree(int l,int r,NODE *&k){
k=new NODE;
if(l==r){
k->maxsum=k->rsum=k->lsum=k->sum=a[l];
return ;
}
int mid=(l+r)>>1;
maketree(l,mid,k->ls);
maketree(mid+1,r,k->rs);
k->maintain();
}
inline NODE* Query(int l,int r,int x,int y,NODE *k){
if(l>=x&&r<=y){
return k;
}
int mid=(l+r)>>1;
NODE *t;
if(y<=mid) t=Query(l,mid,x,y,k->ls);
else if(x>mid) t=Query(mid+1,r,x,y,k->rs);
else{
NODE *t1=Query(l,mid,x,y,k->ls),*t2=Query(mid+1,r,x,y,k->rs);
t=new NODE;
t->sum=t1->sum+t2->sum;
t->lsum=max(t1->lsum,t1->sum+t2->lsum);
t->rsum=max(t2->rsum,t2->sum+t1->rsum);
t->maxsum=max(t1->rsum+t2->lsum,max(t2->maxsum,t1->maxsum));
}
return t;
}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
maketree(1,n,root);
m=read();
for(int i=1;i<=m;i++){
int l,r;
l=read();r=read();
printf("%d\n",Query(1,n,l,r,root)->maxsum) ;
}
return 0;
}
七.
问题描述
现在你有一个长度为n的序列A,你需要做如下的操作:修改A[i]的值,或者给出x和y询问max{Ai + .. + Aj | x <= i <= j <= y},也就是在x和y之间的最大连续和。
输入文件
输入的第 1 行包含一个整数 n 表示序列长度。
接下来一行包含n个整数,分别是A[1], A[2], …, A[n]。
接下来一行包含一个整数m,表示询问个数。
接下来每行一个询问:
0 x y : 将 A[x]修改为y
1 x y : 询问x到y的最大连续和
输出文件
对于每个询问1 x y,输出一行为要求的答案。
输出样例
4
1 2 3 4
4
1 1 3
0 3 -3
1 2 4
1 3 3
输出样例
6
4
-3
#include
#include
#include
using namespace std;
const int INF=2147483640;
int n,m;
int a[200005];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct NODE{
int maxsum,sum,lsum,rsum,b;
NODE *ls,*rs;
NODE():ls(NULL),rs(NULL),b(INF){}
inline void maintain(){
sum=ls->sum+rs->sum;
lsum=max(ls->lsum,ls->sum+rs->lsum);
rsum=max(rs->rsum,rs->sum+ls->rsum);
maxsum=max(ls->rsum+rs->lsum,max(ls->maxsum,rs->maxsum));
}
inline void pushdown(int l,int r){
int mid=(l+r)>>1;
if(b!=INF){
ls->maxsum=ls->lsum=ls->rsum=ls->sum=(mid-l+1)*b;
rs->maxsum=ls->lsum=ls->rsum=rs->sum=(r-(mid+1)+1)*b;
b=INF;
}
}
}*root;
inline void maketree(int l,int r,NODE *&k){
k=new NODE;
if(l==r){
k->maxsum=k->rsum=k->lsum=k->sum=a[l];
return ;
}
int mid=(l+r)>>1;
maketree(l,mid,k->ls);
maketree(mid+1,r,k->rs);
k->maintain();
}
inline void Modify(int l,int r,int x,int y,int z,NODE *k){
if(l>=x&&r<=y){
k->maxsum=k->sum=k->rsum=k->lsum=(r-l+1)*z;
return ;
}
int mid=(l+r)>>1;
if(y<=mid) Modify(l,mid,x,y,z,k->ls);
else if(x>mid) Modify(mid+1,r,x,y,z,k->rs);
else Modify(l,mid,x,y,z,k->ls),Modify(mid+1,r,x,y,z,k->rs);
k->maintain();
}
inline NODE* Query(int l,int r,int x,int y,NODE *k){
if(l>=x&&r<=y){
return k;
}
int mid=(l+r)>>1;
NODE *t;
if(y<=mid) t=Query(l,mid,x,y,k->ls);
else if(x>mid) t=Query(mid+1,r,x,y,k->rs);
else{
NODE *t1,*t2;
t=new NODE;
t1=Query(l,mid,x,y,k->ls);
t2=Query(mid+1,r,x,y,k->rs);
t->sum=t1->sum+t2->sum;
t->lsum=max(t1->lsum,t1->sum+t2->lsum);
t->rsum=max(t2->rsum,t2->sum+t1->rsum);
t->maxsum=max(t1->rsum+t2->lsum,max(t1->maxsum,t2->maxsum));
}
return t;
}
int main(){
n=read();for(int i=1;i<=n;i++) a[i]=read();
maketree(1,n,root);
m=read();
for(int i=1;i<=m;i++){
int ord,x,y;
ord=read();x=read();y=read();
if(ord==0){
Modify(1,n,x,x,y,root);
// maketree(1,n,root);
}
else if(ord==1){
printf("%d\n",Query(1,n,x,y,root)->maxsum);
}
}
return 0;
}
八.
问题描述
给出一个n(n<=50000)元素组成的序列a1,a2…..an,你是任务是设计一种数据结构,支持以下两种操作:
(1)modify(x,y,v) 把ax到ay的值全部修改为v
(2)query(x,y) 计算子序列ax到ay的元素和、最小值、最大值
输入文件
输入的第 1 行包含一个整数 n 表示序列长度
接下来一行包含n个整数,分别是A[1], A[2], …, A[n]
接下来一行包含一个整数m,表示询问个数
接下来每行一个询问:
0 x y v : 将ax到ay的值全部修改为v
1 x y : 询问x到y的区间和、最小值、最大值
输出文件
对于每个询问1 x y,输出区间和、区间最小值、区间最大值。
样例输入
10
1 -2 3 9 4 -2 1 9 3 5
3
1 2 6
0 2 5 3
1 2 8
样例输出
12 -2 9
20 -2 9
#include
#include
#include
#include
using namespace std;
const int INF=2147483647;
int n,m;
int a[100005];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct NODE{
int lsum,rsum,maxx,minn,v,sum,b;
NODE *ls,*rs;
NODE():ls(NULL),rs(NULL),v(0),b(INF){}
inline void maintain(){
sum=ls->sum+rs->sum;
maxx=max(ls->maxx,rs->maxx);
minn=min(ls->minn,rs->minn);
}
inline void pushdown(int l,int r){
int mid=(l+r)>>1;
if(b!=INF){
ls->maxx=ls->minn=rs->maxx=rs->minn=ls->b=rs->b=b;
ls->v=rs->v=0;
ls->sum=(mid-l+1)*b;rs->sum=(r-(mid+1)+1)*b;
b=INF;
}
if(v){
ls->maxx+=v;
rs->maxx+=v;
ls->minn+=v;
rs->minn+=v;
ls->sum+=(mid-l+1)*v;
rs->sum+=(r-(mid+1)+1)*v;
ls->v+=v;
rs->v+=v;
v=0;
}
}
}*root;
inline void maketree(int l,int r,NODE *&k){
k=new NODE;
if(l==r){
k->minn=k->maxx=k->sum=a[l];
return ;
}
int mid=(l+r)>>1;
maketree(l,mid,k->ls); maketree(mid+1,r,k->rs);
k->maintain();
}
inline void add(int L,int R,int x,int y,int z,NODE *k){
if(L>=x && R<=y){
k->v+=z;
k->maxx+=z;
k->minn+=z;
k->sum+=(R-L+1)*z;
return ;
}
k->pushdown(L,R);
int mid=(L+R)>>1;
if(y<=mid) add(L,mid,x,y,z,k->ls);
else if(x>mid) add(mid+1,R,x,y,z,k->rs);
else add(L,mid,x,y,z,k->ls),add(mid+1,R,x,y,z,k->rs);
k->maintain();
return ;
}
inline int Query(int L,int R,int x,int y,NODE *k){
if(L>=x&&R<=y) return k->sum;
int mid=(L+R)>>1,t;
k->pushdown(L,R);
if(y<=mid) t=Query(L,mid,x,y,k->ls);
else if(x>mid) t=Query(mid+1,R,x,y,k->rs);
else t=Query(L,mid,x,y,k->ls)+Query(mid+1,R,x,y,k->rs);
return t;
}
inline int Query2(int L,int R,int x,int y,NODE *k){
if(L>=x&&R<=y) return k->minn;
int mid=(L+R)>>1,t;
k->pushdown(L,R);
if(y<=mid) t=Query2(L,mid,x,y,k->ls);
else if(x>mid) t=Query2(mid+1,R,x,y,k->rs);
else t=min(Query2(L,mid,x,y,k->ls),Query2(mid+1,R,x,y,k->rs));
return t;
}
inline int Query3(int L,int R,int x,int y,NODE *k){
if(L>=x&&R<=y) return k->maxx;
int mid=(L+R)>>1,t;
k->pushdown(L,R);
if(y<=mid) t=Query3(L,mid,x,y,k->ls);
else if(x>mid) t=Query3(mid+1,R,x,y,k->rs);
else t=max(Query3(L,mid,x,y,k->ls),Query3(mid+1,R,x,y,k->rs));
return t;
}
inline void Modify(int L,int R,int x,int y,int z,NODE *k){
if(L>=x&&R<=y){
k->v=0;
k->maxx=k->minn=k->b=z;
k->sum=(R-L+1)*z;
return ;
}
k->pushdown(L,R);
int mid=(L+R)>>1;
if(y<=mid) Modify(L,mid,x,y,z,k->ls);
else if(x>mid) Modify(mid+1,R,x,y,z,k->rs);
else Modify(L,mid,x,y,z,k->ls),Modify(mid+1,R,x,y,z,k->rs);
k->maintain();
}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
maketree(1,n,root);
m=read();
for(int i=1;i<=m;i++){
int ord,x,y,v;
ord=read();x=read();y=read();
if(ord==0){
v=read();
// add(1,n,x,y,v,root);
Modify(1,n,x,y,v,root);
}
else{
printf("%d ",Query(1,n,x,y,root));
printf("%d ",Query2(1,n,x,y,root));
printf("%d\n",Query3(1,n,x,y,root));
}
}
}