参考讲解:https://hihocoder.com/problemset/problem/1586

树状数组位数区间最值(单点修改+区间最值查询)


描述

You are given a list of integers a0, a1, …, a2^k-1.

You need to support two types of queries:

1. Output Minx,y∈[l,r] {ax∙ay}.

2. Let ax=y.

输入

The first line is an integer T, indicating the number of test cases. (1≤T≤10).

For each test case:

The first line contains an integer k (0 ≤ k ≤ 17).

The following line contains 2k integers, a0, a1, …, a2^k-1 (-2k ≤ ai < 2k).

The next line contains a integer  (1 ≤ Q < 2k), indicating the number of queries. Then next Q lines, each line is one of:

1. 1 l r: Output Minx,y∈[l,r]{ax∙ay}. (0 ≤ l ≤ r < 2k)

2. 2 x y: Let ax=y. (0 ≤ x < 2k, -2k ≤ y < 2k)

输出

For each query 1, output a line contains an integer, indicating the answer.


样例输入

1
3
1 1 2 2 1 1 2 2
5
1 0 7
1 1 2
2 1 2
2 2 2
1 1 2


样例输出

1
1
4


【树状数组代码】:

#include <iostream>  
#include <stdio.h>  
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAX=1<<18;
struct tree{
	int n;
	int a[MAX],Max[MAX],Min[MAX];//Max维护最大值 ,Min最小
	void init(int N)
	{
		n=N;
		for(int i=0;i<=N;i++)
			Max[i]=-(Min[i]=INF);
	}
	void update(int x,int num)//单点更新 
	{
		a[x]=num;
	    while(x<=n)
	    {
	        Min[x]=Max[x]=a[x];
	        int lx=x&-x;
	        for(int i=1;i<lx;i<<=1)
	        {
	        	Max[x]=max(Max[x],Max[x-i]);
	            Min[x]=min(Min[x],Min[x-i]);
			}
	        x+=x&-x;
	    }
	}
	int Qmax(int x,int y)//[x,y]最大值
	{  
	    int ans=-INF; 
	    while(y>=x)  
	    {  
	        ans=max(a[y], ans);  
	        y--;
	        for(;y-(y&-y)>=x;y-=(y&-y))
	            ans=max(Max[y],ans);  
	    }  
	    return ans;  
	}
	int Qmin(int x, int y)
	{  
	    int ans=INF;  
	    while(y>=x)
	    {
	        ans=min(a[y],ans);
	        y--;
	        for(;y-(y&-y)>= x; y-=(y&-y)  )
	            ans=min(Min[y],ans);  
	    }
	    return ans;
	}  
}C;
int main()  
{  
    int n,m,i,j,x,y,ch,T;
    cin>>T;
    while(T--)
    {
		scanf("%d",&n);
		n=(1<<n);
		C.init(n);//初始化 
        for (i=1;i<=n;i++)
        {  
            scanf("%d",&y);
			C.update(i,y);
        }
        cin>>m;
        for (i=1; i<=m; i++)  
        {  
            scanf("%d%d%d",&ch,&x,&y);
            if (ch == 1)  
            {
                int u=C.Qmax(x+1,y+1);
                int v=C.Qmin(x+1,y+1);
                ll ans;
                if(u<=0)ans=(ll)u*u;
                else if(v>=0)ans=v*v;
                else ans=(ll)u*v;
                printf("%lld\n",ans);
            }
            if(ch==2)
                C.update(x+1,y);
        }
    }
    return 0;
}


【线段树代码】:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAX=220;
const int INF=0x3f3f3f3f;
int a[MAX+2],n;
int sum[2*MAX],Min[2*MAX],Max[2*MAX];
void creat(int root=1,int l=1,int r=n)
{
    if(l==r)//叶子节点
    {
        sum[root]=a[l];
        Max[root]=a[l];
        Min[root]=a[l];
        return;
    }
    int mid=(l+r)/2;
    creat(root*2,l,mid);   //左子树的创建
    creat(root*2+1,mid+1,r); //右子树
    sum[root]=sum[root*2]+sum[root*2+1];
    Max[root]=max(Max[root*2],Max[root*2+1]);
    Min[root]=min(Min[root*2],Min[root*2+1]);
}
void update(int kl,int kr,int num, int root=1,int l=1,int r=n)//区间[kl,kr]修改
{
    if(l==r){
        a[l]=num;
        sum[root]=num;
        Max[root]=num;
        Min[root]=num;
        return;
    }
    int mid=(l+r)/2;
    if(kl<=mid)
        update(kl,kr,num,root*2,l,mid);
    if(kr>mid)
        update(kl,kr,num,root*2+1,mid+1,r);
    sum[root]=sum[root*2]+sum[root*2+1];
    Max[root]=max(Max[root*2],Max[root*2+1]);
    Min[root]=min(Min[root*2],Min[root*2+1]);
}
int Qsum(int left,int right, int root=1,int l=1,int r=n)//区间和
{
    if(left<=l&&r<=right)
        return sum[root];
    int mid=(l+r)/2;
    int t1=0,t2=0;
    if(left<=mid)
        t1=Qsum(left,right,root*2,l,mid);//区间在左子树
    if(right>mid)
        t2=Qsum(left,right,root*2+1,mid+1,r);//在右子树
    return t1+t2;
}
int Qmax(int left,int right, int root=1,int l=1,int r=n)//查询区间[left,right]的最大值
{
    if(left<=l&&r<=right)
        return Max[root];
    int mid=(l+r)/2;
    int t1=-INF,t2=-INF;
    if(left<=mid)
        t1=Qmax(left,right,root*2,l,mid);//区间在左子树
    if(right>mid)
        t2=Qmax(left,right,root*2+1,mid+1,r);//在右子树
    return max(t1,t2);
}
int Qmin(int left,int right, int root=1,int l=1,int r=n)//查询区间[left,right]最小值
{
    if(left<=l&&r<=right)
        return Min[root];
    int mid=(l+r)/2;
    int t1=INF,t2=INF;
    if(left<=mid)
        t1=Qmin(left,right,root*2,l,mid);//区间在左子树
    if(right>mid)
        t2=Qmin(left,right,root*2+1,mid+1,r);//在右子树
    return min(t1,t2);
}
int main()
{
    int m,i,j,x,y,ch,T;
    cin>>T;
    while(T--)
    {
        scanf("%d",&n);
        n=(1<<n);
        for (i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        creat(1,1,n);
        cin>>m;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&ch,&x,&y);
            if(ch==1)
            {
                int u=Qmax(x+1,y+1);
                int v=Qmin(x+1,y+1);
                ll ans;
                if(u<=0)ans=(ll)u*u;
                else if(v>=0)ans=(ll)v*v;
                else ans=(ll)u*v;
                printf("%lld\n",ans);
            }
            if(ch==2)
                update(x+1,x+1,y);
        }
    }
    return 0;
}