zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Linux中/etc目录下passwd和shadow文件
    Linux基本命令
    Linux目录结构说明与基本操作
    如何用虚拟机VMware Workstation安装CentOs-7
    VPP源码分析及流程图
    VPP环境搭建及配置
    【转】智能指针的交叉引用问题及解决方法
    二叉树的前 中 后 层序遍历
    排序算法
    C和C++的一些区别
  • 原文地址:https://www.cnblogs.com/Aragaki/p/11153989.html
Copyright © 2011-2022 走看看