Codeforces 1175F The Number of Subpermutations

做法①: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;
}
View Code

做法②:哈希(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;
}
View Code

 

posted @ 2019-07-08 21:36  Aragaki  阅读(176)  评论(0编辑  收藏  举报