ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛 I
#1586 : Minimum
-
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
描述
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.
解题思路:线段树模板题……一开始以为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;
}