神仙题。官方题解搬运工,尽量加上自己的理解使得这篇题解更好理解。
题意:
首先会发现,每个数只要在一个区间 \([l_v,r_v]\) 中那么一定有解,并且一定是 \(v\) 更大的会套住 \(v\) 更小的形成一个树形结构。有点反直觉但是手玩一下确实是对的。
先考虑怎么计算,如果有这个的话直接从小到大扫一遍,计算 \((r_v-l_v+1-[t<v且l_v\le l_t\le r_t\le r_v 的个数])\)。
考虑证明这个东西,我们将在给出算法的同时说明这个事情。
我们考虑从 \(v\) 小到大进行建立,假设我们已经完成了 \(v=m\) 的建立,现在我们要建立 \(v=m + 1\),我们可以先找到已经建立的包含 \(p_v\) 的区间,\(p_v\) 是数 \(v\) 的下标。
那么考虑现在这个区间 \([L,R]\),我们尝试去扩展这个区间,我们先认为 \(>m+1\) 的都可以随意重排,反正可以交换的情况是对于 \(v=m +1\) 没有区别。
对于 \(L\) 右侧的数,我们需要满足这些条件:
-
对于 \(v=m+1\),我们希望他尽量接近 \(L\)。
-
对于 \(v\le m\),我们希望他在第一条的前提下尽量靠近 \(L\)。
对于 \(L\) 左侧:
-
有恰好一个 \(>v\) 的数接近 \(L\)。
-
对于 \(v\le m\),我们希望他在第一条的前提下尽量靠近 \(L\)。
然后如果这样能交换,那么就说明我们可以跨过 \(L\)。
但是显然这些条件还不够,太难判断了。但是我们注意到 \([L,R]\) 区间内的数一定不能放出去,所以对于右侧的第二条限制,其实等同于我们尽量往左放,左侧的同理。
那么其实我们再考虑 \(v=m+1\),我们要让他尽量接近 \(L\),其实等于我们所有 \(v\le m\) 的尽量往右放,给我的 \(v=m+1\) 去腾出来位置让他尽量接近,对于左侧需要的 \(v>m+1\) 的同理。
但是这里还有一个特殊的情况,如果左侧的尽量往右放,右侧的尽量往左放之后,我的整个区间并没有 \(K\) 长,那么显然也是不能交换的。这里注意,因为我只需要没有第二个 \(v>m+1\) 的出现,所以对于未放置的第二个之前的都可以使用,而不是未放置第一个之前的。
根据这个算法,因为我们可以控制 \(v\le m\) 的放置来控制我交换到的位置,所以我们确实说明了可交换到的是一个区间且可以随便放。
然后根据上述过程去维护,我这里用的是 set,那么就可以在 \(O(n\log n)\) 的时间解决。
结合代码应该会更好理解一些:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2.5e5 + 5, mod = 998244353;
int a[maxn], p[maxn], n, k;
#define lowbit(x) (x & (-x))
struct Tree_array {int tr[maxn];void add(int x, int val) {while(x <= n)tr[x] += val, x += lowbit(x);}int query(int x) {int ans = 0;while(x)ans += tr[x], x -= lowbit(x);return ans;}
} tree;
set<int> s, tor, tol; //目前剩下的端点,全部尽量往右放还剩下的位置,往左放剩下的
bool chk(int x) { // 判断是否可以经过 [x-1,x]int rx = *tor.lower_bound(x), ry = *++tol.lower_bound(x); // 尽量往右放,第一个还可以放的位置;尽量往左放,只要不碰到第二个 v > m + 1 的都可以取int lx = *--tol.lower_bound(x), ly = *----tor.lower_bound(x);if(rx <= n && lx >= 1 && rx - lx < k && ry - ly - 1 - 2 >= k - 2) //保证两个位置之间至少 < k可以操作,并且有足够多的填入return 1;return 0;
}
signed main() {cin >> n >> k;for (int i = 1; i <= n; i++)cin >> a[i], p[a[i]] = i;for (int i = 0; i <= n + 1; i++)s.insert(i), tol.insert(i), tor.insert(i);int ans = 1;for (int i = 1; i <= n; i++) {set<int>::iterator itl = --s.upper_bound(p[i]), itr = s.upper_bound(p[i]);while(chk(*itl))itl = --s.erase(itl);while(chk(*itr))itr = s.erase(itr);tor.erase(--tor.lower_bound(*itr)), tol.erase(tol.lower_bound(*itl)); // 往右往左放ans = ans * (*itr - *itl - (tree.query(*itr - 1) - tree.query(*itl - 1))) % mod;tree.add(p[i], 1);}cout << ans << endl;return 0;
}