做法①:RMQ(预处理NLOGN+后续跳跃蜜汁复杂度)
满足题意的区间的条件转换:
1.长度为R-L+1则最大值也为R-L+1
2.区间内的数不重复
当RMQ(L,R)!=R-L+1时 因为已经保证了 i~r[i] 之间的数不重复 则RMQ(L,R)必定大于当前的R-L+1 所以我们需要至少跳到 i+RMQ(L,R)-1
#include<bits/stdc++.h> using namespace std; int a[1000005], r[1000005]; int where[1000005]; int dp[1000005][20]; int n,ans; void rmqinit() { for (int i = 1; i <= n; i++) { dp[i][0] = a[i]; } for (int j = 1; (1 << j) <= n; j++) { for (int i = 1; i + (1 << j) - 1 <= n; i++) { dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]); } } } int rmq(int l, int r) { int x = 0; while (1 << (x + 1) <= r - l + 1) { x++; } return max(dp[l][x], dp[r - (1 << x) + 1][x]); } int main() { cin >> n; r[n] = n; for (int i = 1; i <= n; i++) { cin >> a[i]; } rmqinit(); where[a[n]] = n; for (int i = n - 1; i >= 1; i--) { if (where[a[i]]) { r[i] = min(r[i + 1], where[a[i]] - 1); } else { r[i] = r[i + 1]; } where[a[i]] = i; } for(int i=1;i<=n;i++) { int j=i; while(j<=r[i]) { if(rmq(i,j)==j-i+1) { ans++; j++; } else { j=i+rmq(i,j)-1; } } } cout<<ans<<endl; }
做法②:哈希(O(N))
给每个数一个哈希值 从左往右扫一次 从右往左扫一次 如果扫到1就停下检查答案直到数组末尾或碰到下一个1 注意单个的1每次只能算一次
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<ll, ll> p; const int MAXN = 300005; int n, ans, T; ll num[MAXN]; p hsh[MAXN];//每个数的哈希值 p prehsh[MAXN];//数组前缀的哈希值 p sumhsh[MAXN];//1~X数的哈希值 p update(p x, p y) { //哈希异或计算 x.first ^= y.first; x.second ^= y.second; return x; } int getans(int x) { ll maxn = 1; //从第一个1到下一个1或末端所经过的所有数最大值 ll r = x; //现在遍历到哪个数 int now = 0; //目前符合条件的答案 while (r < n && num[r + 1] != 1) { //如果还没有到头且下一个不是1 r++; //到达下一个 maxn = max(maxn, num[r]); //维护最大值 if (r - maxn >= 0) { p xx; xx = update(prehsh[r], prehsh[r - maxn]); if (xx == sumhsh[maxn]) { now++; //如果最大值不超过遍历过的数的数量且子串的哈希值与sumhsh[maxn]对应则答案++ } } } return now; } int main() { ll x = 0; cin >> n; for (int i = 1; i <= n; i++) { cin >> num[i]; x ^= num[i]; } for (int i = 1; i <= n; i++) { //分配1~n对应每个值的哈希值 hsh[i].first = x ^ rand(); hsh[i].second = x ^ rand(); sumhsh[i] = hsh[i]; if (i != 1) { sumhsh[i] = update(sumhsh[i], sumhsh[i - 1]); //计算数字1~i的哈希前缀 } } for (T = 1; T <= 2; T++) { for (int i = 1; i <= n; i++) { prehsh[i] = hsh[num[i]]; if (i != 1) { prehsh[i] = update(prehsh[i], prehsh[i - 1]); //计算数组前i个数的哈希前缀 } } for (int i = 1; i <= n; i++) { if (num[i] == 1) { if (T == 1) { //单个1只能从单个方向计算一次 ans++; } ans += getans(i); } } reverse(num + 1, num + 1 + n); } cout << ans << endl; return 0; }