P10371 「LAOI-4」石头 题解

原题链接:P10371

首先我们设 \(l_{i,0/1}\) 表示 \(i\) 左边的第一,二个比 \(a_i\) 大的数的位置。\(r_{i,0/1}\) 同理。

考虑一个区间 \([L,R]\) 在什么时候满足条件,设 \(p,q\) 分别为区间中最大 / 次大值的位置,我们分三种情况讨论。

  • 情况一:\(L < p < R\)

考虑从 \(L,R\) 开始选数分别会选到哪些数。

  1. \(L\) 开始,会选到 \((l_{p,0},R]\) 这些数。因为当要选 \(a_p\) 的时候,一定会先选完 \((l_{p,0},p-1]\) 这些数(因为这些数都比 \(a_p\) 小),而且肯定不会选到 \(l_{p,0}\),因为区间 \((l_{p,0},R]\) 中的所有数都比它小。

  2. \(R\) 开始,会选到 \([L,r_{p,0})\) 这些数,证明同理。

会发现如果 \([L,R]\) 这个区间合法的充要条件为 \(L-l_{p,0}=r_{p,0}-R\)\(L > l_{p,0},R < r_{p,0}\)(否则 \(a_p\) 就不是区间最大值了),所以我们可以枚举每一个 \(p\),那么当前 \(p\) 的贡献(\(L,R\) 可取的位置数)为 \(\min(r_{p,0} - p - 1,p - l_{p,0} - 1)\),时间复杂度 \(O(n)\)

  • 情况二:(\(L = p\)\(R=p\))且 \(L \le q \le R\)

假设 \(L=p\)\(R=p\) 时同理),还是考虑从 \(L,R\) 开始选数分别会选到哪些数。

  1. \(L=p\) 开始,会选到 \((l_{q,1},R]\) 这些数。因为当要选 \(a_q\) 的时候,一定会先选完 \((l_{q,1},q-1]\) 这些数(因为这些数都比 \(a_q\) 小(除了 \(a_p\),但是 \(a_p\) 第一次已经被选了)),而且肯定不会选到 \(l_{q,1}\),因为区间 \((l_{q,1},R]\) 中的所有数都比它小(除了 \(a_p\),但是 \(a_p\) 第一次已经被选了)。

  2. \(R\) 开始,会选到 \([L,r_{p,0})\) 这些数。因为第一次没有选 \(a_p\),所以和情况一相同。

会发现如果 \([L=p,R]\) 这个区间合法的充要条件为 \(p-l_{q,1}=r_{p,0}-R\)。我们枚举每一个 \(q\),会发现 \(q\) 只能对应到唯一一个 \(p\)\(l_{q,0}\),否则不满足(\(p,q\) 分别为区间中最大 / 次大值的位置)这个条件。那么 \(p-l_{q,1}\) 的值是固定的,于是我们可以直接求出唯一可能的 \(R\),判断是否合法即可。\(R\) 合法当且仅当 \(q \le R < r_{q,0}\)。时间复杂度 \(O(n)\)

  • 情况三:\(L=R\)

显然每一个区间都满足条件,这种情况的总方案数为 \(n\),在输出的时候加上即可。


那么最后要解决的问题就是如何求 \(l,r\) 数组了,这是一个较为经典的问题。

暴力用树状数组求的话复杂度是 \(O(n \log n)\) 的,应该不能通过。其实我们可以用链表维护这一信息。我们从小到大考虑每一个数,已经考虑过了的数就直接在链表中删除该节点。那么考虑到每一个数的时候剩下的数一定都比它大,于是直接用链表的前驱后继计算答案即可,时间复杂度 \(O(n)\)

signed main() {
	cin >> n >> S,srand_(S,n);
	for(int i = 1;i <= n;i++) 
		pos[a[i]] = i,lst[i] = i - 1,nxt[i] = i + 1;
	for(int i = 1;i <= n;i++) {
		int id = pos[i];
		l[id][0] = lst[id],l[id][1] = lst[lst[id]];
		r[id][0] = nxt[id],r[id][1] = nxt[nxt[id]];
		nxt[lst[id]] = nxt[id],lst[nxt[id]] = lst[id];//在链表上删除 a[id]
	}
	for(int i = 1;i <= n;i++) //情况一
		ans += max(0ll,1ll * min(r[i][0] - i - 1,i - l[i][0] - 1));
	for(int i = 1;i <= n;i++) { //情况二
		int q = i,p1 = l[q][0],p2 = r[q][0];
		int pos1 = r[p1][0] - (p1 - l[q][1]);
		int pos2 = l[p2][0] + (r[q][1] - p2);
		if(pos1 >= q && pos1 < r[q][0]) ans++; //L = P
		if(pos2 <= q && pos2 > l[q][0]) ans++; //R = P
	} cout << ans + n;return 0; // 情况三
} 
posted @ 2024-04-26 11:18  Creeper_l  阅读(7)  评论(0编辑  收藏  举报