题解 UVA12299 【RMQ with Shifts】

题目简述:

给出n个数q个询问。对于query(a,b),输出区间(a,b)的数的最小值;对于shift(a0,a1,a2,......,an),则将第a1个数的值赋给a0,第a2个数赋给a1......,第an个数赋给an-1,第a0个数赋给an

主要思路:三叉 线段树 (单点修改,区间求最值)

其实就是一道比较裸的线段树模板题,因为题目中shift操作的字符长不超过30 char,也就是说没几个数,我们完全可以直接暴力的每个操作。

我们可以用一个数组a[]来存储原数组,这样便于交换位置时直接赋值。记得把一开始的值先存下来QAQ

然后比较麻烦的就是字符串的操作,实际也不是很麻烦,就像写快读那样写就好。

我这里用了一个自己琢磨的三叉线段树(动态开点),如有不适,自己码正常的线段树,或者在我的板子库里找找线段树那一栏。

code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <map>
using namespace std;
#define go(i, j, n, k) for(int i = j; i <= n; i += k)
#define fo(i, j, n, k) for(int i = j; i >= n; i -= k)
#define rep(i, x) for(int i = h[x]; i; i = e[i].nxt)
#define mn 500010
#define inf 1 << 30
#define ll long long
#define root 1, n, rot
#define lson l, m1, z[rt].l
#define mson m1 + 1, m2, z[rt].m
#define rson m2 + 1, r, z[rt].r
#define bson l, r, rt
inline int read(){
	int x = 0, f = 1; char ch = getchar();
	while(ch > '9' || ch < '0') { if(ch == '-') f = -f; ch = getchar(); }
	while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}

struct tree {
	int l, m, r;
	int x;
} z[mn << 1]; // 不需要4倍,2倍就好(动态开点的服利)
int cnt, n, m, rot, a[mn]; // rot 就是根节点编号(其实就是1,,,)
inline void update(int rt) { // 更新这里似乎不能直接全部求最小,可能不存在这个节点
	z[rt].x = inf;
	if(z[rt].l) z[rt].x = min(z[z[rt].l].x, z[rt].x);
	if(z[rt].m) z[rt].x = min(z[z[rt].m].x, z[rt].x);
	if(z[rt].r) z[rt].x = min(z[z[rt].r].x, z[rt].x);
} 
void build(int l, int r, int &rt) {
	rt = ++cnt;
	if(l == r) { z[rt].x = a[l]; return; }
	if(r - l == 1) { build(l, l, z[rt].l), build(r, r, z[rt].r); update(rt); return; }
	int t = (r - l + 1) / 3 + ((r - l + 1) % 3 == 2 ? 1 : 0), m2 = l + t + t - 1, m1 = l + t - 1;
	build(lson), build(mson), build(rson);
	update(rt);
}
void modify(int l, int r, int rt, int now, int v) {
	if(l == r) { z[rt].x = v; return; }
	if(r - l == 1) {
		if(now == l) modify(l, l, z[rt].l, now, v);
		else if(now == r) modify(r, r, z[rt].r, now, v);
		update(rt);
		return;
	}
	int t = (r - l + 1) / 3 + ((r - l + 1) % 3 == 2 ? 1 : 0), m2 = l + t + t - 1, m1 = l + t - 1;
	if(now <= m1) modify(lson, now, v);
	else if(now <= m2) modify(mson, now, v);
	else modify(rson, now, v);
	update(rt);
}
int query(int l, int r, int rt, int nowl, int nowr) {
	if(nowl <= l && r <= nowr) return z[rt].x;
	if(r - l == 1) { // 如果只能有两个子节点
		int ans = 0;
		if(nowl <= l && l <= nowr) ans += z[z[rt].l].x;
		if(nowl <= r && r <= nowr) ans += z[z[rt].r].x;
		return ans;
	}
	int t = (r - l + 1) / 3 + ((r - l + 1) % 3 == 2 ? 1 : 0), m2 = l + t + t - 1, m1 = l + t - 1;
	int ans = inf, f1 = 0, f2 = 0;
	if(nowl <= m1) ans = min(query(lson, nowl, nowr), ans), f1 = 1;
	if(m2 < nowr)  ans = min(query(rson, nowl, nowr), ans), f2 = 1;
	if(!(f1 || f2) || (f1 && m1 < nowr) || (f2 && nowl <= m2))
		ans = min(query(mson, nowl, nowr), ans);
	return ans;
}

// 以上代码与普通线段树的单点修改区间求最值没啥差别
// 我只是写成了三叉的而已
char s[35];
int b[35];

inline void solve() {
	n = read(), m = read();
	go(i, 1, n, 1) a[i] = read();
	build(root);
	go(i, 1, m, 1) {
		scanf("%s", s);
		if(s[0] == 'q') {
			int now = 5, x = 0, y = 0;
			while(s[++now] <= '9' && s[now] >= '0')
				x = x * 10 + s[now] - '0';
            // 相当于输入x
			while(s[++now] <= '9' && s[now] >= '0')
				y = y * 10 + s[now] - '0';
            // 相当于输入y
			printf("%d\n", query(root, x, y));
		} else {
			int nn = 0, now = 5, x = 0, last = 0;
//			printf("Shift %d:\n", i);
			while(s[now] != ')') {
				x = 0;
				while(s[++now] <= '9' && s[now] >= '0')
					x = x * 10 + s[now] - '0';
				b[++nn] = x;
//				cout << x << " ";
			}
//			puts("");
			int t = a[b[1]];  
			go(j, 1, nn - 1, 1) {
				modify(root, b[j], a[b[j + 1]]);
				a[b[j]] = a[b[j + 1]];
			} 
			modify(root, b[nn], t);
			a[b[nn]] = t;
		}
//		printf("Case %d:\n", i); 
//		go(j, 1, n, 1) 
//			printf("%d ", a[j]);
//		puts("");
	}
}
int main(){
	solve();
	return 0;
}

还有一点,这个题只有一个测试点,别去找 0 0 就是了。

posted @ 2019-03-22 23:17  yizimi  阅读(119)  评论(0编辑  收藏  举报