hihoCoder

来源:互联网 发布:p值计算软件 编辑:程序博客网 时间:2024/06/03 13:52

 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.

样例输入
131 1 2 2 1 1 2 251 0 71 1 22 1 22 2 21 1 2
样例输出
114
题意:给你一串数字和一系列操作,对于每一个询问操作,我们要找出区间内任意两个数乘积的最小值。修改操作就是单点修改。


解题思路:线段树模板题……一开始以为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;}




原创粉丝点击