HDU:6701-Make Rounddog Happy (啓發式分治)

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6701

解題心得:

  • 這個題首先要明白是用分治去解決,也就是說如果區間[l,r][l,r]之間最大值在位置pospos,那麼可以枚舉當左端點爲i(l<=i<=pos)i(l<=i<=pos)得到包含pospos的右端點(固定左端點算貢獻),按照題目中所給的關係式和不重複的規定得到符合條件右端點爲一段區間[L,R][L,R],如果L<=RL<=R,那麼說明這個ii點對於最後的答案有貢獻,那麼最終答案加上(RL+1R-L+1)。算出當前[l,r][l,r]之後遞歸下去計算[l,mid1][l,mid-1][mid+1,r][mid+1,r]
  • 其次有個問題就是怎麼可以直接找到區間[l,r][l,r]的最大值和最大值的位置,這個可以先處理出一個ST表,這樣就可以直接得到了。並且關於一點ii的不重複左右區間也可以先預處理出來。
  • 最後分治的時候有個問題就是當所給出的nn個數爲不重複升序排列的時候,那麼分治僅僅是每次將右端點向左移動一位並且每次枚舉固定左端點複雜度就成了O(logn2)O(logn^{2})。這個時候就需要啓發式優化,也就是說我們找到區間[l,r][l,r]之間最大值在位置pospos,那麼算貢獻的時候可以固定左端點計算右端點的個數,也可以固定右端點計算左端點的個數,固定左/右端點需要枚舉的區間分別可以是[l,mid][l, mid][mid,r][mid,r],那麼假設枚舉規則是哪個區間小枚舉哪個區間,這時遞歸下去的時候複雜度越壞計枚舉算次數越少,這就將最壞的情況進行了限制,真的是非常精妙的優化啊。


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+100;

int num[maxn], maxSt[maxn][30], posSt[maxn][30], dR[maxn], dL[maxn], n, k;
ll ans = 0;

void init() {
    ans = 0;
    scanf("%d%d", &n, &k);
    for(int i=1;i<=n;i++) scanf("%d", &num[i]);
}

void getStTable() {
    for(int i=1;i<=n;i++) {
        maxSt[i][0] = num[i];
        posSt[i][0] = i;
    }

    for(int j=1;j<=30;j++) {
        for(int i=1;i+(1<<j)-1<=n;i++) {
            int Max1 = maxSt[i][j - 1];
            int Max2 = maxSt[i + (1 << (j - 1))][j - 1];
            if (Max1 >= Max2) {
                maxSt[i][j] = Max1;
                posSt[i][j] = posSt[i][j - 1];
            } else {
                maxSt[i][j] = Max2;
                posSt[i][j] = posSt[i+(1<<(j-1))][j-1];
            }
        }
    }
}

bool vis[maxn];
void getLRPos() {
    int pos = 2;
    vis[num[1]] = true;
    for(int i=1;i<=n;i++) {
        while(pos <= n && !vis[num[pos]]) {
            vis[num[pos]] = true;
            pos++;
        }
        dR[i] = pos-1;
        vis[num[i]] = false;
    }
    memset(vis, 0, sizeof vis);
    pos = n-1;
    vis[num[n]] = true;
    for(int i=n;i>=1;i--) {
        while(pos >= 1 && !vis[num[pos]]) {
            vis[num[pos]] = true;
            pos--;
        }
        dL[i] = pos+1;
        vis[num[i]] = false;
    }
}

int getPos(int l, int r) {
    int Log = 0, Pow = 1;
    while(Pow <= (r-l+1)) {
        Pow <<= 1;
        Log++;
    }
    Log--;

    int Max1 = maxSt[l][Log];
    int Max2 = maxSt[r-(1<<Log)+1][Log];
    if(Max1 >= Max2) return posSt[l][Log];
    else return posSt[r-(1<<Log)+1][Log];
}

void getCnt(int l, int r) {
    if(l > r) return ;
    int mid = getPos(l, r);
    if(r - mid > mid - l) {
        for (int i = l; i <= mid; i++) {
            int L = max(mid, num[mid] - k + i - 1);
            int R = min(dR[i], r);

            if (L <= R) ans += R - L + 1;
        }
    } else {
        for(int i=r;i>=mid;i--) {
            int R = min(mid, k+1+i-num[mid]);
            int L = max(dL[i], l);

            if(L <= R) ans += R-L+1;
        }
    }
    getCnt(l, mid-1);
    getCnt(mid+1, r);
}

int main() {
//    freopen("1.in.txt", "r", stdin);
    int t; scanf("%d", &t);
    while(t--) {
        init();
        getStTable();
        getLRPos();
        getCnt(1, n);
        printf("%lld\n", ans);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章