hihoCoder - 1586 Minimum (2017 ACM-ICPC 亚洲区 (北京赛区) 网络赛 I)

 ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛 I


#1586 : Minimum

时间限制: 1000ms
单点时限: 1000ms
内存限制: 256MB

描述

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, -2≤ 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
题意:给你一串数字和一系列操作,对于每一个询问操作,我们要找出区间内任意两个数乘积的最小值。修改操作就是单点修改。


解题思路:线段树模板题……一开始以为x!=y,结果想用主席树去求第k大,后来才反应过来,x可以等于y,这样的话就很简单了。只需找出最大最小值即可。如果最小值为负数,最大值为正数,那么相乘即可。如果最小值为正数,那么就是最小值的平方。如果最大值为负数,那么就是最大值的平方。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int INF=1<<30;
typedef long long int ll;
const int maxn=200005;

int treemax[maxn<<2];//线段树数组,看你要存什么
int treemin[maxn<<2];//线段树数组,看你要存什么

int A[maxn];//原数组,下标1~n

//更新节点信息,这里是求最值
void pushup(int rt){
    treemax[rt]=max(treemax[rt<<1],treemax[rt<<1|1]);//<<1为*2.<<1|1为*2+1,即左子树和右子树
    treemin[rt]=min(treemin[rt<<1],treemin[rt<<1|1]);//<<1为*2.<<1|1为*2+1,即左子树和右子树

}

//建树
void build(int l,int r,int rt){//l,r表示当前区间,rt表示当前区间在线段树数组中的位置
    if(l==r){//若到达叶子结点
        treemax[rt]=A[l];//将该位置存原数组的值
        treemin[rt]=A[l];//将该位置存原数组的值
        return;
    }
    int m=(l+r)>>1;//>>1等于/2
    //递归建树
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);//建完左右子树后,更新当前节点的值
}


//点修改,即A[L]=C,要同时修改相关区间的值,与建树同理,其实就是建树的过程
void update(int L,int C,int l,int r,int rt){
    if(l==r){//若到达叶节点,则修改叶节点的值
        treemin[rt]=C;
        treemax[rt]=C;
        return;
    }
    int m=(l+r)>>1;

    //根据L判断是往哪个子树递归修改
    if(L<=m)
        update(L,C,l,m,rt<<1);//左子树
    else
        update(L,C,m+1,r,rt<<1|1);//右子树

    pushup(rt);//子节点更新完了,那么可以更新自己了,即从下而上修改,建树同理
}


//查询,这里为求最值,LR代表要查询的区间,lr代表当前区间,rt表示当前节点在数组中的实际位置
int querymax(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R)//如果当前区间在查询区间内,直接返回当前存的值
        return treemax[rt];

    int m=(l+r)>>1;



    //累加求答案
    int ANS=-INF;
    if(L<=m)//如果左子区间与[L,R]有重叠,就递归左子树,右子树同理。
        ANS=max(ANS,querymax(L,R,l,m,rt<<1));
    if(R>m)
        ANS=max(ANS,querymax(L,R,m+1,r,rt<<1|1));
    return ANS;
}

int querymin(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R)//如果当前区间在查询区间内,直接返回当前存的值
        return treemin[rt];

    int m=(l+r)>>1;



    //累加求答案
    int ANS=INF;
    if(L<=m)//如果左子区间与[L,R]有重叠,就递归左子树,右子树同理。
        ANS=min(ANS,querymin(L,R,l,m,rt<<1));
    if(R>m)
        ANS=min(ANS,querymin(L,R,m+1,r,rt<<1|1));
    return ANS;
}


int main(){



    int t;
    int n,m;
    scanf("%d",&t);

    while(t--){

        scanf("%d",&n);
        n=1<<n;


        for(int i=1;i<=n;i++)
        {
            scanf("%d",&A[i]);
        }

        build(1,n,1);

        scanf("%d",&m);

        int a,b,c;
        for(int i=0;i<m;i++){


            scanf("%d%d%d",&c,&a,&b);
            a++;


            if(c==1){
                b++;
                ll mmax=querymax(a,b,1,n,1);
                ll mmin=querymin(a,b,1,n,1);


                if(mmax<=0)printf("%lld\n",mmax*mmax);
                else if(mmin>=0) printf("%lld\n",mmin*mmin);
                else printf("%lld\n",mmin*mmax);

            }
            else{
                update(a,b,1,n,1);
            }


        }
    }

    return 0;
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值