炒币

#3798. 炒币

吐槽:因为考试的时候一时脑残,想出了一个 DM 思路,于是和它奋战了一整天。

Solution One

思路

不难想到构造一个折线图,取每一个单调递减区间的首节点和尾节点,具体见 蔡景行

代码:

#include <bits/stdc++.h>
#define Maxn 200005

using namespace std;

int n, a[Maxn], tag = 0;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> n;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
	}
	
	a[0] = -2147483647, a[n + 1] = 2147483647;
	for (int i = 1; i <= n; i ++) {
		if ((a[i] >= a[i - 1] && a[i + 1] <= a[i] && tag == 0) || (a[i] <= a[i - 1] && a[i + 1] >= a[i] && tag == 1)) {
			cout << 1 << " ", tag = !tag;
		} else {
			cout << 0 << " ";
		}
	}
	return 0;
}

Solution Two

这是一个***的搜索思路。Warning:请熟悉题目后再来理解这篇题解。

前置定义

区间: 任意 \(l,r(1 \le l \le r \le n)\) 中间的所有节点构成的集合叫做一个 区间

折点: 为了达到最终的目标(即:换出最多的 \(\texttt{RMB}\)),应该进行兑换的点叫做 折点

区间的奇偶性: 为了达到最终的目标(即:换出最多的 \(\texttt{RMB}\)),一个区间内理论上应有的 折点 的个数的 奇偶性 即为这个 区间的奇偶性

畸形(非畸形): 为了达到最终的目标(即:换出最多的 \(\texttt{RMB}\)),一个区间最终换出的钱币的类型决定了这个 区间 是否 畸形

  • 若最终换出 \(\texttt{BTB}\)​(溴麝香草酚蓝),则称这个区间是 畸形 的。
  • 若最终换出 \(\texttt{RMB}\),则称这个区间是 非畸形 的。

思路

蔡景行 知(屎粪礼貌),我们应在汇率最高点兑换成 \(\texttt{BTB}\),而在汇率最低点兑换成 \(\texttt{RMB}\)

对于一个区间,我们可以找到其中汇率最高的点,以这个点分成两段,继续 \(\texttt{Dfs}\)

直到最终这个区间内只有一个或者两个节点时,可以直接判断,时间复杂度 \(O(能过)\)

正确性证明: 注意,证明是没有的,但具体有无正确性,我不知道(反正就是 AC 了,时间不算慢)。

具体见代码:(主要是我不会解释了)

// 抽象派代码
#include <bits/stdc++.h>
#define Maxn 200005

using namespace std;

int n, a[Maxn];
int lg__[Maxn], maxl[Maxn][20], minl[Maxn][20];
bool vis[Maxn];

int Remax(int i, int j) {
	return (a[i] > a[j] ? i : j);
} int Remin(int i, int j) {
    return (a[i] > a[j] ? j : i);
} // 找节点

int ST_max(int l, int r) {
	int log__ = lg__[r - l + 1];
	return Remax(maxl[l][log__], maxl[r - (1 << log__) + 1][log__]);
}

void dfs(int l/*左端点*/, int r/*右端点*/, bool fl1/*0 -- 偶性  1 -- 奇性*/, bool fl2/*0 -- 非畸形  1 -- 畸形*/) {
	if (l == r) { // 只有一个点,要么选,要么不选
		vis[l] = fl1; // 这可以根据奇偶性判断
		return ;
	} else if (l + 1 == r) { // 有两个点
		if (fl1) { // 如果为奇性,则只能选一个
			vis[(fl2 == 0 ? Remin(l, r) : Remax(l, r))] = 1; // 根据要换出啥判断选哪一个
		} if (!fl1 && ((a[l] >= a[r] && fl2 == 0)/*如果非畸形且呈递减*/ ||
                       (a[l] < a[r] && fl2 == 1)/*如果畸形且递增*/)) { // 如果为偶性,要么都选,要么都不选
			vis[l] = vis[r] = 1;
		} return ;
	}
	
    // 烧脑部分
	if (fl1 == 0) { // 要取偶数个
		int Max = ST_max(l, r);
		if (fl2 == 0) { // 非畸形(吸收RMB,释放RMB)
			if (Max == l) { // 如果第一个是最大值
				vis[Max] = 1, dfs(Max + 1, r, 1, 0);
                /*
                	啃腚要选第一个,然后搜索[Max + 1, r]
                	[Max + 1, r]应为奇性,因为输出RMB,所以非畸形
                */
			} else if (Max == r){ // 如果最后一个是最大值
				dfs(l, Max - 1, 0, 0);
                /*
                	最后一个啃腚没法选,直接搜索[l, Max - 1]
                	因为不选,所以[l, Max - 1]性质(泛指奇偶性与是否畸形,下文统一用 性质 代替)应和[l, r]一样
                */
			} else { // 最大值在中间
				vis[Max] = 1, dfs(l, Max - 1, 0, 0), dfs(Max + 1, r, 1, 0);
                /*
                	啃腚要选Max,然后分成两个区间
                	因为此处为汇率峰值,所以要把这个点和区间[Max + 1, r]相提并论(这点自己体会)
                	经过简单的思考易得区间[l, Max - 1]的性质与区间[l, r]一样,而区间[Max + 1, r]应为非畸形的奇性
                */
			} // 这只是最简单,最明晰的一类
		} else { // 畸形
			if (Max == l) { // 如果第一个是最大值
				dfs(Max + 1, r, 0, 1);
                /*
                	因为是畸形的偶性,且第一个就是最大值,所以不选最大值
                	然后就是区间[Max + 1, r],因为没有兑换,所以区间[Max + 1, r]性质应与区间[l, r]一样
                */
			} else if (Max == r){ // 如果最后一个是最大值
				vis[Max] = 1, dfs(l, Max - 1, 1, 0);
                /*
                	最后一个是最大值,而且是畸形的偶性,所以最后一个是最大值肯定会选
                	至于区间[l, Max - 1],因为换了一次,所以奇偶性与区间[l, r]相反,是否畸形更不用说
                */
			} else { // 最大值在中间
				vis[Max] = 1, dfs(l, Max - 1, 1, 0), dfs(Max + 1, r, 0, 1);
                /*
                	记住:最大值在中间不论怎样都要选(*)
                	然后就是分成了两个区间:
                	[l, Max - 1]要求换出RMB,非畸形
                	[Max + 1, r]要求换出BTB,畸形。区间[Max + 1, r]吸收BTB,换出BTB,为偶性
                	所以由(偶([l, r]) - 偶([Max + 1, r]) - 1(Max) = 奇)知区间[l, Max - 1]为奇性
                */
			}
		}
	} else { // 要取奇数个
		int Max = ST_max(l, r);
		if (fl2 == 0) { // 非畸形
			if(Max == l) { // 如果第一个是最大值
				dfs(Max + 1, r, 1, 0);
                /*
                	不能取最大值,不是最优的。
                	直接搞区间[Max + 1, r],因为没有兑换,所以区间[Max + 1, r]性质与区间[l, r]一样
                */
			} else if(Max == r) { // 如果最后一个是最大值
				dfs(l, Max - 1, 1, 0);
                /*
                	也不能兑换。搞区间[l, Max - 1],因为没有兑换,所以区间[l, Max - 1]性质与区间[l, r]一样
                */
			} else {
				vis[Max] = 1, dfs(l, Max - 1, 1, 0), dfs(Max + 1, r, 1, 0);
                /*
                	易知要兑换
                	分成两个区间,略
                */
			}
		} else { // 畸形
			if (Max == l) { // 如果第一个是最大值
				vis[Max] = 1, dfs(Max + 1, r, 0, 1);
                /*
                	易知要兑换。搞区间[Max + 1, r],略
                */
			} else if (Max == r){ // 如果最后一个是最大值
				vis[Max] = 1, dfs(l, Max - 1, 0, 0);
                /*
                	易知要兑换。搞区间[l, Max - 1],略
                */
			} else { // 最大值在中间
				vis[Max] = 1, dfs(l, Max - 1, 0, 0), dfs(Max + 1, r, 0, 1);
                /*
                	易知要兑换。分成两个区间,略
                */
			}
		}
	}
} // 好久没有见到这么折磨人的DFS了
// DFS后面的注释打的不耐烦了,请读者自行理解

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	lg__[0] = -1; // 叛逆
	for (int i = 1; i <= 2e5; i ++) {
		lg__[i] = lg__[i >> 1] + 1;
	}
	
	cin >> n;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i], maxl[i][0] = i;
	}
	
	for (int i = 1; i <= 20; i ++) {
		for (int j = 1; j + (1 << i) - 1 <= n; j ++) {
			maxl[j][i] = Remax(maxl[j][i - 1], maxl[j + (1 << (i - 1))][i - 1]);
		}
	} // 用 ST表 找到区间最大值
	
	dfs(1, n, 0, 0);
    /*最初的区间为了换出RMB,应该换偶数次,并且非畸形*/
	
    cout << vis[1];
	for (int i = 2; i <= n; i ++) {
		cout << " " << vis[i];
	}
	return 0;
}

离谱:

  • 其实打这篇代码时,本人也不知道为什么要用最大值去将左右两个区间分开(或许是一心想着利益最大化吧)。现在,本人盲猜用最小值将两个区间分开也有可能 \(\texttt{AC}\)
  • 本人打这篇代码前,并没有正确性证明(现在也没有),望大佬补充证明或 \(\texttt{HACK}\)掉这篇毒瘤代码。
  • 本人与 \(\texttt{DZB}\) 调这篇代码时,一人贡献了一组极小的数据,分别卡出来一个错误。于是,一份 \(\texttt{AC}\) 代码出现了。

结语:一道 \(\colorbox{#F39C11}{\color{white}普及-}\) 硬是被我做成了 \(\colorbox{#3498DB}{\color{white}提高+/省选−}\),真的服了。

posted @ 2024-08-07 16:25  BLM-dolphin  阅读(12)  评论(0)    收藏  举报