宝石专家 题解 +小结

1.前言

我是 s b sb sb T 4 T4 T4 我最开始写的是分块,卡了半天常数,浪费了 1.5 h + 1.5h^+ 1.5h+,最后花了 30 m i n 30min 30min C D Q CDQ CDQ真·暴力比正解复杂 。所以之后还是不要想卡评测机这件事,卡常骗分又不能增强实力,又浪费时间。(分块的被 h a c k hack hack 了, 氧化钙

2.题解

C D Q CDQ CDQ
将所有相邻的特征值相同的宝石全部找出来,记作一个三元组(为了用三维偏序) ( i d x i , l i , r i ) (idx_i,l_i, r_i) (idxi,li,ri)(后面就称为修改区间)。 i d x idx idx表示时间戳(即操作顺序), l i l_i li r i r_i ri 表示相邻的特征值相同的宝石的下表标

我们的询问 [ l , r ] [l, r] [l,r] 相当于寻找 m i n ( r i − l i ) ( l ≤ l i , r i ≤ r ) min (r_i-l_i)(l \leq l_i, r_i \leq r) min(rili)(lli,rir),用三维偏序就行了。

按照 l l l 排序,现在 [ l , m i d ] [l, mid] [l,mid] 的指针为 i i i [ m i d + 1 , r ] [mid + 1, r] [mid+1,r] 的指针为 j j j,则我们需要找到 m i n ( r p − l p ) ( l j ≤ l p , r p ≤ r j , p ∈ [ l , i ] ) min (r_p-l_p)(l_j \leq l_p, r_p \leq r_j, p \in [l,i]) min(rplp)(ljlp,rprj,p[l,i]) l l l 的条件已经满足了,所以用树状数组,维护满足 r i ≤ r r_i \leq r rir 的最小区间即可

#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define LL long long 
template <typename T>
void read (T &x) {
	x = 0; T f = 1;
	char ch = getchar ();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar ();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + ch - '0';
		ch = getchar ();
	}
	x *= f;
}
template <typename T>
void write (T x) {
	if (x < 0) {
		x = -x;
		putchar ('-');
	}
	if (x < 10) {
		putchar (x + '0');
		return;
	}
	write (x / 10);
	putchar (x % 10 + '0');
}
template <typename T>
void print (T x, char ch) {
	write (x); putchar (ch);
}
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }

const int Maxn = 2 * 1e5 + 10;
const int Inf = 0x3f3f3f3f;

int n, m;
int a[Maxn + 5], bak[Maxn + 5];

vector <int> v;
int rev[Maxn + 5];
int Find (int x) {
	return lower_bound (v.begin (), v.end (), x) - v.begin ();
}

int len_q, len_ans;
int ans[Maxn + 5];
struct Node {
	int op, l, r, val, id;
	Node () {}
	Node (int OP, int L, int R, int VAL, int ID) {
		op = OP; l = L; r = R; val = VAL; id = ID;
	}
	//op = 0,表示有一个修改区间 [l, r],长度为 val (即 r - l, 但懒得打 r - l),id 无用
	//op = 1,表示有一个询问区间 [l, r],val 记录答案,id 记录 ta 是第几个询问。
}q[Maxn * 2 + 5], tem[Maxn * 2 + 5];
struct Arrey {
	int BIT[Maxn + 5];
	void Init () {
		memset (BIT, 0x3f, sizeof BIT);
	}
	int lowbit (int x) { return x & -x; }
	void Update (int Index, int x) {
		//在 Index 处增加一个点 x 
		for (int i = Index; i <= Maxn; i += lowbit (i))
			BIT[i] = Min (BIT[i], x);
	}
	int Query (int Index) {
		//返回 <= i 的 r 的最小值 
		int res = Inf;
		for (int i = Index; i >= 1; i -= lowbit (i))
			res = Min (res, BIT[i]);
		return res;
	}
	void Clean (int Index) {
		//清空 Index 处的修改 
		for (int i = Index; i <= Maxn; i += lowbit (i))
			BIT[i] = Inf;
	}
}Tr;
bool operator < (Node x, Node y) {
//重载运算符 
	if (x.l != y.l) return x.l > y.l;
	else if (x.r != y.r) return x.r < y.r;
	else return x.op < y.op;
}
void CDQ (int l, int r) {
//三维偏序模板 
	if (l == r) return;
	int mid = (l + r) >> 1;
	CDQ (l, mid); CDQ (mid + 1, r);
	
	int i = l, j = mid + 1, k = l;
	while (i <= mid && j <= r) {
		if (q[i] < q[j]) {
			if (q[i].op == 0)
				Tr.Update (q[i].r, q[i].val);
			tem[k++] = q[i++];
		}
		else {
			if (q[j].op == 1)
				q[j].val = Min (q[j].val, Tr.Query (q[j].r));
			tem[k++] = q[j++];
		}
	}
	while (i <= mid) {
		if (q[i].op == 0)
			Tr.Update (q[i].r, q[i].val);
		tem[k++] = q[i++];
	}
	while (j <= r) {
		if (q[j].op == 1)
			q[j].val = Min (q[j].val, Tr.Query (q[j].r));
		tem[k++] = q[j++];
	}
	for (i = l; i <= mid; i++) {
		if (q[i].op == 0)
			Tr.Clean (q[i].r);
	}
	for (i = l; i <= r; i++) q[i] = tem[i];
}

signed main () {
	freopen ("5.in", "r", stdin);
	Tr.Init ();
	read (n); read (m);
	for (int i = 1; i <= n; i++) {
		read (a[i]);
		v.push_back (a[i]);
	}
	
	sort (v.begin (), v.end ());
	v.erase (unique (v.begin (), v.end ()), v.end ());
	for (int i = 1; i <= n; i++)
		rev[i] = Find (a[i]);
	for (int i = 1; i <= n; i++) {
		if (bak[rev[i]] != 0)
			q[++len_q] = Node (0, bak[rev[i]], i, i - bak[rev[i]], -1);
		//有一个相邻相同点对 (bak[rev[i]], i) 
		bak[rev[i]] = i;
	}
	for (int i = 1; i <= m; i++) {
		int l, r; read (l); read (r);
		q[++len_q] = Node (1, l, r, Inf, ++len_ans);
		//查询 [l, r] 
	}
	CDQ (1, len_q);
	for (int i = 1; i <= len_q; i++) {
		if (q[i].op == 1)
			ans[q[i].id] = q[i].val;
	}
	for (int i = 1; i <= m; i++) {
		if (ans[i] > n) {
			putchar ('-');
			putchar ('1');
			putchar ('\n');
		}
		else print (ans[i], '\n');
	}
	return 0;
}

3.小结

0 m i n − 5 m i n 0min-5min 0min5min:写完 T 1 T1 T1 (AC

5 m i n − 15 m i n 5min-15min 5min15min:想了一下 T 2 T2 T2,思路有问题,码量太大,于是去看 T 3 T3 T3(还好没上头,吸取了昨天晚上一直淦T4,结果思路错了,浪费了 2 h + 2h^+ 2h+ 的教训

15 m i n − 25 m i n 15min-25min 15min25min:写完 T 3 T3 T3,手玩几组小数据,没有问题(AC

25 m i n − 26 m i n 25min-26min 25min26min T 4 T4 T4 一看就会,和小 Y 的房间做法一样,分块乱搞就行了。(但是时间复杂度超了,而且没发现

26 m i n − 50 m i n 26min-50min 26min50min T 4 T4 T4 写完,调过所有样例,搞了一组大数据, 8 s 8 s 8s 才跑过

50 m i n − 150 m i n ( 左 右 ) 50min-150min(左右) 50min150min() T 4 T4 T4 卡常(太浪费时间了)。

150 m i n − 160 m i n 150min-160min 150min160min:回头看 T 2 T2 T2,想到了怎样优化代码,很快就打出来了(AC

160 m i n − 165 m i n 160min-165min 160min165min:思考 T 4 T4 T4 & 人生

165 m i n − 205 m i n 165min-205min 165min205min:写出 C D Q CDQ CDQ 正解,压线做完所有题目。(AC

205 m i n − 210 m i n 205min-210min 205min210min:小黄鸭检查

之前受到了剪枝优化带师的刺激(现代豪宅暴力连虚边 70 pts),理论和实际时间复杂度的差别直接震惊我,以至于我考试时还想靠一些剪枝来水掉 qwq,这种做法是不可取的,到了最后追求高分的时候,肯定是想正解,剪枝暴力永远都不能让人达到顶尖水平。虽然距离顶尖水平还很遥远,但是开始的几步一定不能走偏,所以之后一定不能再浪费如此多时间去剪枝卡常了啊 o w o owo owo.

posted @ 2021-06-26 12:05  C2022lihan  阅读(44)  评论(0编辑  收藏  举报