题目链接:(https://vjudge.net/problem/UVA-12299)
分析:
两种操作,查询[x,y]之间的最小值,第二种是把给定位置的元素循环左移,若是最前面的,则移到最后面。将给定位置的节点值记录一下,再循环左移就行。注意建树的过程是n*logn的时间复杂度。另外注意输入的处理,%6s代表输入6个字符。这次真是吃了血亏,在每个操作输入之间要加getchar()吃掉回车,我没加,因为我的电脑运行没毛病,结果wrong成狗,记得要吃掉回车
代码如下:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=100000+5;
int minv[maxn<<2];
vector<int>value_v; //记录给定位置的值
vector<int>p; //保存给定位置
//获得给定位置p的元素的编号
int Get_ID(int p,int l,int r,int rt)
{
if(l==r)return rt;
int m=(l+r)>>1;
if(p<=m)Get_ID(p,l,m,rt<<1);
else Get_ID(p,m+1,r,rt<<1|1);
}
//建树
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%d",&minv[rt]); //建树的过程中输入
return ;
}
int m=(l+r)>>1; //x>>1==x/2
build(l,m,rt<<1); //向左走 x<<1==x*2
build(m+1,r,rt<<1|1); //向右走,(x<<1|1)==x*2+1
minv[rt]=min(minv[rt<<1],minv[rt<<1|1]);
}
//更新,将p位置的值改为c
void update(int p,int c,int l,int r,int rt)
{
if(l==r)
{
minv[rt]=c;
return ;
}
int m=(l+r)>>1;
if(p<=m)update(p,c,l,m,rt<<1);
if(p>m)update(p,c,m+1,r,rt<<1|1);
minv[rt]=min(minv[rt<<1],minv[rt<<1|1]);
}
//查询,返回区间最小值
int query(int ll,int rr,int l,int r,int rt)
{
if(ll<=l&&rr>=r)
return minv[rt];
int m=(l+r)>>1;
int cnt=INF;
if(ll<=m)
cnt=min(cnt,query(ll,rr,l,m,rt<<1));
if(rr>m)
cnt=min(cnt,query(ll,rr,m+1,r,rt<<1|1));
return cnt;
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
build(1,n,1);
char op[50];
while(q--)
{
getchar();
scanf("%6s",op);
if(op[0]=='q')
{
int x,y;
scanf("%d,%d%)",&x,&y);
printf("%d\n",query(x,y,1,n,1));
}
else if(op[0]=='s')
{
p.clear();
char c;
int x;
while(scanf("%d%c",&x,&c))
{
p.push_back(x);
if(c!=',')
break;
}
for(int i=0; i<p.size(); i++)
{
int id=Get_ID(p[i],1,n,1); //找到给定位置元素的编号
value_v.push_back(minv[id]);
}
//循环左移
for(int i=0; i<p.size(); i++)
{
update(p[i],value_v[(i+1)%p.size()],1,n,1);
}
value_v.clear(); //注意清空,防止影响下一次操作
}
}
}