• [学习笔记]启发式分治


    启发式分治

    给定n个数,求满足某种条件的点对数目或最大权值,而这个最大权值与点对(a,b)的区间[a,b]的区间最大/最小值有关。
    那么这时就可以考虑分治,对于区间[L,R],找到最小/大值所在位置,然后处理横跨最小/大值所在位置的点对,然后递归处理子区间。

    HUD-Make Rounddog Happy

    • 求有多少区间,满足其中每个数都不同,并且区间最大值大于k
    • 预处理区间最大值,以及每个数往左往右最远不相同的位置
    • 暴力处理区间小的,然后递归
    #pragma GCC optimize(2)
    
    #include <bits/stdc++.h>
    
    #define ll long long
    using namespace std;
    const int maxn = 3e5 + 7;
    int stmax[maxn][20], pos[maxn][20], poww[20], logg[maxn];
    int n, s[maxn], p, L[maxn], R[maxn];
    bool vis[maxn];
    
    void get_st() {
        poww[0] = 1;
        for (int i = 1; i < 20; ++i) poww[i] = poww[i - 1] << 1;
        for (int i = 2; i <= n; ++i) logg[i] = logg[i >> 1] + 1;
        int temp = 1;
        for (int j = 1; j <= logg[n]; ++j) {
            for (int i = 1; i <= n - temp * 2 + 1; ++i) {
                if (stmax[i][j - 1] >= stmax[i + temp][j - 1]) {
                    stmax[i][j] = stmax[i][j - 1];
                    pos[i][j] = pos[i][j - 1];
                } else {
                    stmax[i][j] = stmax[i + temp][j - 1];
                    pos[i][j] = pos[i + temp][j - 1];
                }
            }
            temp <<= 1;
        }
    }
    
    int query_pos(int l, int r) {
        int k = logg[r - l + 1];
        if (stmax[l][k] >= stmax[r - poww[k] + 1][k]) {
            return pos[l][k];
        } else {
            return pos[r - poww[k] + 1][k];
        }
    }
    
    void get_diff() {
        vis[s[1]] = 1;
        int r = 2;
        for (int i = 1; i <= n; ++i) {
            while (r <= n && !vis[s[r]]) {
                vis[s[r]] = 1;
                r++;
            }
            vis[s[i]] = 0;
            R[i] = r - 1;
        }
        vis[s[n]] = 1;
        r = n - 1;
        for (int i = n; i >= 1; --i) {
            while (r >= 1 && !vis[s[r]]) {
                vis[s[r]] = 1;
                r--;
            }
            vis[s[i]] = 0;
            L[i] = r + 1;
        }
    }
    
    ll ans = 0;
    
    void solve(int l, int r) {
        if (l > r) return;
        int mid = query_pos(l, r);
        if (r - mid > mid - l) {
            for (int i = l; i <= mid; ++i) {
                int d = max(mid, s[mid] - p + i - 1);
                int dd = min(R[i], r);
                if (dd < d)continue;
                ans += dd - d + 1;
            }
        } else {
            for (int i = r; i >= mid; --i) {
                int d = min(mid, p + 1 + i - s[mid]);
                int dd = max(L[i], l);
                if (dd > d)continue;
                ans += d - dd + 1;
            }
        }
        solve(l, mid - 1);
        solve(mid + 1, r);
    }
    
    int main() {
        int _;
        scanf("%d", &_);
        while (_--) {
            scanf("%d%d", &n, &p);
            for (int i = 1; i <= n; ++i) {
                scanf("%d", &s[i]);
                stmax[i][0] = s[i];
                pos[i][0] = i;
                vis[i] = 0;
            }
            get_st();
            get_diff();
            ans = 0;
            solve(1, n);
            printf("%lld
    ", ans);
        }
        return 0;
    }
    

    Removing Stones

    • 题意最后可以化为求有多少个区间,满足区间最大值小于区间和的两倍

    • 最大值还是ST表求,然后二分求满足区间和的位置

    #include <bits/stdc++.h>
     
    #define ll long long
    using namespace std;
    const int maxn = 3e5 + 7;
    int stmax[maxn][20], pos[maxn][20], poww[20], logg[maxn];
    int n, s[maxn];
    ll sum[maxn],sum1[maxn];
     
    void get_st() {
        poww[0] = 1;
        for (int i = 1; i < 20; ++i) poww[i] = poww[i - 1] << 1;
        for (int i = 2; i <= n; ++i) logg[i] = logg[i >> 1] + 1;
        int temp = 1;
        for (int j = 1; j <= logg[n]; ++j) {
            for (int i = 1; i <= n - temp * 2 + 1; ++i) {
                if (stmax[i][j - 1] >= stmax[i + temp][j - 1]) {
                    stmax[i][j] = stmax[i][j - 1];
                    pos[i][j] = pos[i][j - 1];
                } else {
                    stmax[i][j] = stmax[i + temp][j - 1];
                    pos[i][j] = pos[i + temp][j - 1];
                }
            }
            temp <<= 1;
        }
    }
     
    int query_pos(int l, int r) {
        int k = logg[r - l + 1];
        if (stmax[l][k] >= stmax[r - poww[k] + 1][k]) {
            return pos[l][k];
        } else {
            return pos[r - poww[k] + 1][k];
        }
    }
     
    ll ans = 0;
    int erfen1(int l,int r,ll x){
        while(l<r){
            int mid=(l+r)>>1;
            if(sum[mid]>=x) r=mid;else l=mid+1;
        }
        return l;
    }
    int erfen2(int l,int r,ll x){
        while(l<r){
            int mid=(l+r+1)>>1;
            if(sum1[mid]<=x)l=mid; else r=mid-1;
        }
        return l;
    }
    void solve(int l, int r) {
        if (l >= r)return;
        int mid = query_pos(l, r);
        if (mid - l < r - mid) {
            for (int i = l; i <= mid; ++i) {
                int pp=erfen1(mid,r+1,s[mid]*2ll+sum1[i]);
                //cout<<pp<<" "<<s[mid]*2ll+sum1[i]<<" "<<mid<<endl;
                ans+=r-pp+1;
            }
        } else {
            for (int i = r; i >= mid; --i) {
                int pp=erfen2(l-1,mid,sum[i]-s[mid]*2ll);
                ans+=pp-l+1;
            }
        }
        solve(l, mid - 1);
        solve(mid + 1, r);
    }
     
    int main() {
        int _;
        scanf("%d", &_);
        while (_--) {
            scanf("%d", &n);
            for (int i = 1; i <= n; ++i) {
                scanf("%d", &s[i]);
                stmax[i][0] = s[i];
                pos[i][0] = i;
                sum[i]=sum[i-1]+s[i];
                sum1[i]=sum[i-1];
            }
            get_st();
            ans=0;
            solve(1,n);
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
    不要忘记努力,不要辜负自己 欢迎指正 QQ:1468580561
  • 相关阅读:
    POJ ACM题分类
    HDU 4438 Hunters (概率 & 期望)
    HDU 1042 N!
    HDU 1073 Online Judge
    PKU 1006 Biorhythms (中国剩余定理 * *)
    HDU 1047 Integer Inquiry
    HDU 2710 Max Factorv (素数模板 & 多种解法)
    HDU A + B Problem II 1002
    第6期(江西省吉安市永丰县)县长手机信箱工作简报(自吹自擂政绩,自圆其说)
    Send mail from ASP.NET using your gmail account
  • 原文地址:https://www.cnblogs.com/smallocean/p/11432735.html
Copyright © 2020-2023  润新知