1. 程式人生 > 其它 >2021暑假 HDU中超 第五場 1009

2021暑假 HDU中超 第五場 1009

被這個題折磨了好幾天 從比賽中的奇怪思路繞到洛谷原題,從分治繞到樹狀陣列,還先去搞了半天模板題,可謂是曲線救國

被這個題折磨了好幾天
從比賽中的奇怪思路繞到洛谷原題,從分治繞到樹狀陣列,還先去搞了半天模板題,可謂是曲線救國

2021暑假 HDU中超 第五場 1009

Array

Time Limit: 15000/8000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 235 Accepted Submission(s): 48

題意

給你一個序列\(a[1...n]\)​,求存在絕對眾數的子區間個數。

絕對眾數指:區間中出現次數最多的那個數,出現次數嚴格大於區間長度的一半。

Sample Input
1
10
3303 70463 3303 3303 3303 70463 3303 3303 70463 70463
Sample Output
47
考場思路:

玄學而虛假的主席樹亂搞做法

曲線救國:

首先注意到這是洛谷原題(RNM退錢)

分治做法:(只能過洛谷過不了比賽題)

對於區間 \([l,r]\)​,中間值 \(mid\)​, 我們需要統計經過 \(mid\)​ 的所有合法區間 \([l', r']\)

而如果 \([l',r']\)​​ 擁有絕對眾數,那麼 \([l', mid]\)\([mid+1, r']\) 中至少有一個有絕對眾數

那麼可以從 \(mid\) 往兩邊搜,統計每種數出現的次數,如果搜到某個位置時,當前數出現次數大於區間長度的一半,則這個數有可能成為絕對眾數​。我們首先 \(O(r-l)\)

地找到所有可能的結果

再依次列舉這些眾數,對於眾數 \(X\)​​,我們可以按投票的思想,把所有的 \(X\)​​​ 記為1,其他數記為-1,計算字首和,這樣可以判斷任意區間是否滿足條件。

若區間 \([l',r']\) 滿足 \(sum[r'] - sum[l'-1] > 0\),則這個區間有絕對眾數 \(X\)

也就是說需要在 \(mid\)​ 左右找到滿足 \(sum[r'] > sum[l'-1]\)​ 的點對個數

至此問題轉化為了一個比較經典的模型,用樹狀陣列搞即可。

程式碼複雜度 \(O(nlog^3n)\) 洛谷能過

但這題直接從1e5加強到1e6,直接T飛

AC做法:

首先記錄每一種數字出現的位置,然後列舉每一種數字,設當前列舉到 \(X\)

然後還是按投票的思想,把所有的 \(X\)​​ 記為1,其他數記為-1,計算字首和 \(sum\)

若區間 \([l',r']\) 滿足 \(sum[r'] - sum[l'-1] > 0\),則這個區間有絕對眾數 \(X\)(和上面做法一樣)

考慮從左到右遍歷,對於當前位置的字首和 \(sum\)​​ ,我們需要求出它左邊比它小的字首和個數

記錄每個字首和出現的次數為 \(cnt[x]\),用樹狀陣列維護​和查詢

複雜度大概是 \(O(n^2logn)\)

想到我們記錄了每個數字出現的位置,那麼是不是可以在遍歷的時候直接跳過一串連續的-1?

跳的時候面臨兩個問題:

  1. 如何把這一串-1位置上的字首和計入 \(cnt\) 陣列
  2. 如何計算這些-1位置對答案的貢獻

對於問題二:

​ 首先回憶一下:位置 \(p\)​​​​​​ 的貢獻,就是 \(p\)​ 前面比 \(sum[p]\)​​​​​ 小的字首和個數

​ 也就是 \(cnt\)​​ 陣列從最小值加到 \(sum[p]\)​​​ 為止的字首和,設這個字首和為 \(ssum\)

\(ssum[p]\)​ 是位置 \(p\)​ 上的答案

​ 而我們需要知道這一連串-1的答案之和

​ 顯然再搞一遍字首和就行了。。。設 \(sssum\) 是這個東西。。。

現在問題被轉化成了:我們要維護 \(cnt\)​ 陣列的字首和的字首和

別急 還沒完

對於問題一,其實很簡單,對 \(cnt\) 陣列搞個差分陣列 \(d\)​ 就好了,做到 \(O(1)\) 插入

那麼只需要維護 \(d\)​​​​ 陣列的字首和的字首和的字首和。。。

其實寫起來沒那麼麻煩

我們只需要一個支援單點修改,三階字首和查詢的資料結構

還是樹狀陣列!

具體樹狀陣列的高階字首和搞法

打包起來會比較好寫一點

struct BitTree{//4行普通樹狀陣列
    ll t[2*maxn];
    int lb(int x) {return x&-x;}
    void add(int p, ll ad){for(int i=p;i<=2*n+20;i+=lb(i)) t[i]+=ad;}
    ll sum(int p){ return p?t[p] + sum(p-lb(p)):0ll;}
};

struct BitTree3{//三階字首和的樹狀陣列
    BitTree bt1, bt2, bt3;
    void add(int p, ll ad){
        bt1.add(p, ad);
        bt2.add(p, ad * p);
        bt3.add(p, ad * p * p);
    }
    ll sum(ll p){ return ((p+1)*(p+2) * bt1.sum(p) - (2*p+3) * bt2.sum(p) + bt3.sum(p)) / 2;}
    void modify(int l, int r, int dt = 1){
        add(l + bs, dt);
        add(r + 1 + bs, -dt);
    }
    ll query(int l, int r){
        if(l > r) return 0;
        return sum(r - 1 + bs) - sum(l - 2 + bs);
    }
};

AC程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#ifdef ACM_LOCAL
const int maxn = 1000040;
#else
const int maxn = 1000040;
#endif

int n; ll bs;
vector<int> pos[maxn];

struct BitTree{//4行樹狀陣列
    ll t[2*maxn];
    int lb(int x) {return x&-x;}
    void add(int p, ll ad){for(int i=p;i<=2*n+20;i+=lb(i)) t[i]+=ad;}
    ll sum(int p){ return p?t[p] + sum(p-lb(p)):0ll;}
};

struct BitTree3{//三階字首和的樹狀陣列
    BitTree bt1, bt2, bt3;
    void add(int p, ll ad){
        bt1.add(p, ad);
        bt2.add(p, ad * p);
        bt3.add(p, ad * p * p);
    }
    ll sum(ll p){ return ((p+1)*(p+2) * bt1.sum(p) - (2*p+3) * bt2.sum(p) + bt3.sum(p)) / 2;}
    void modify(int l, int r, int dt = 1){
        add(l + bs, dt);
        add(r + 1 + bs, -dt);
    }
    ll query(int l, int r){
        if(l > r) return 0;
        return sum(r - 1 + bs) - sum(l - 2 + bs);
    }
};

BitTree3 t;
void solve(){
    cin >> n >> bs;
    for(int i=1;i<=n;i++){
        int xx;
        cin >> xx;
        pos[xx].push_back(i);
    }
    ll ans = 0;
    bs = n + 10;
    for(int i=0;i<=1e6;i++){
        if(!pos[i].size()) continue;
        ll sum = 2 - pos[i][0];
        t.modify(1-pos[i][0], 0);
        t.modify(sum, sum);
        ans++;

        for(int j=1;j<pos[i].size();j++){
            int l = pos[i][j] - pos[i][j-1] - 1;
            ans += t.query(sum - l, sum - 1);
            t.modify(sum - l, sum - 1);
            (sum -= l) += 1;
            ans += t.query(sum, sum);
            t.modify(sum, sum);
        }
        int l = n + 1 - pos[i][pos[i].size()-1] - 1;
        ans += t.query(sum - l, sum - 1);

        //相同操作撤銷
        t.modify(1-pos[i][0], 0, -1);
        sum = 2 - pos[i][0];
        t.modify(sum, sum, -1);
        for(int j=1;j<pos[i].size();j++){
            int l = pos[i][j] - pos[i][j-1] - 1;
            t.modify(sum - l, sum - 1, -1);
            (sum -= l) += 1;
            t.modify(sum, sum, -1);
        }

        pos[i].clear();
    }
    cout << ans << '\n';
}