1. 程式人生 > 其它 >hdu7020 Array(2021杭電暑假多校5)多數投票演算法

hdu7020 Array(2021杭電暑假多校5)多數投票演算法

題意

給一個長度為\(n\)​的陣列\(a\)​,問有多少個子區間存在絕對眾數(即有一個數出現次數大於\(\lfloor\frac{R-L+1}{2}\rfloor\)​)。(\(a_i\le{10}^6,1\le n\le{10}^6\)​)

分析

其實這是道原題:「CodePlus 2017 11 月賽」Yazid 的新生舞會

首先列舉哪個數是絕對眾數,設為\(x\)​​​​,由多數投票演算法我們可以把所有\(x\)​​​​的位置改為\(1\)​​​​,所有非\(x\)​​​​的位置改為\(-1\)​​​​。\(x\)​​​​是區間絕對眾數的區間就是和大於\(0\)​​​的區間(即算字首和的一個二維偏序)。用樹狀陣列維護的複雜度為\(n^2logn\)

​​​​。

我們發現,上面暴力的做法複雜度比暴力\(O(n^2)\)​還高是由於\(-1\)​的點太多了。但是他們大多數不對答案產生貢獻,那麼就可以考慮用到哪些\(-1\)​再在樹狀陣列中對它進行插入/查詢。對於值為\(1\)​的點,直接在樹狀陣列上查詢/插入即可。對於值為\(-1\)​的點,若它的字首和為當前最小值,那麼它之後的一段\(-1\)​的答案一定為\(0\)​,考慮之後用到再延遲插入即可。其他值為\(-1\)​的點同樣直接查詢/插入即可。因為一個\(1\)​最多隻在它之後產生一個非最小值的\(-1\)​且最多在它之前用掉一個延遲更新的\(-1\)​,所以操作次數不超過\(3n\)​。又發現所有延遲更新的區間是不相交的,所以可以\(O(1)\)

​維護延遲更新的段。總時間複雜度為\(O(nlogn)\)​​​​​。

最後考慮到當前字首和的變化一定是\(+1,-1\)​​​或者直接變為最小值的。直接用陣列和一個指標替換樹狀陣列,暴力轉移當前答案即可。

程式碼

\(O(nlogn)\)

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
constexpr int N(1e6+5);
struct Fenwick{
  int cnt[N*2];
  vector<pii>inserted;
  void insert(int p,int v) {
    if(v>0) inserted.push_back({p,v});
    for(p+=N;p<N*2;p+=(p&-p)) {
      cnt[p]+=v;
    }
  }
  int count(int p) {
    int ans=0;
    for(p+=N;p;p-=(p&-p)) {
      ans+=cnt[p];
    }
    return ans;
  }
  void clear() {
    for(pii p:inserted)
      insert(p.first,-p.second);
    inserted.clear();
  }
}fw;

struct Seg{
  vector<pii>segs;
  void add(int l,int r) {
    segs.push_back(pii(l+N,r+N));
  }
  void de(int i) {
    i+=N;
    while(!segs.empty() && segs.back().first<=i){
      int& j=segs.back().first;
      fw.insert(j-N,1);
      j++;
      if(j>segs.back().second)
        segs.pop_back();
    }
  }
  void clear() {
    segs.clear();
  }
}sg;

void solve() {
  int n,mx=0;
  cin>>n;
  vector<int>a(n+1);
  for(int i=1;i<=n;i++) {
    cin>>a[i];
    mx=max(a[i],mx);
  }
  vector<vector<int>>nums(mx+1);
  for(int i=1;i<=n;i++) nums[a[i]].push_back(i);
  ll ans=0;
  for(int num=0;num<=mx;num++) {
    vector<int>&pos=nums[num];
    if(pos.empty()) continue;
    pos.push_back(n+1);
    int minn=0,now=0;
    fw.clear();
    sg.clear();
    fw.insert(0,1);
    for(int i=1,j=0;i<=n;) {
      if(a[i]==num) {
        sg.de(now);
        fw.insert(++now,1);
        ans+=fw.count(now-1);
        j++;
        i++;
      }
      else {
        if(now==minn) {
          int l=now-1,r=now-(pos[j]-i);
          now-=(pos[j]-i);
          sg.add(r,l);
          i=pos[j];
        }
        else {
          fw.insert(--now,1);
          ans+=fw.count(now-1);
          i++;
        }
        minn=min(minn,now);
      }
    }
  }
  cout<<ans<<'\n';
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int T;
  cin>>T;
  while(T--) solve();
  return 0;
}

\(O(n)\)

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
constexpr int N(1e6+5);

struct Array{
  int a[N*2],ans=0;
  vector<int>pos;
  void insert(int i) {
    i+=N;
    a[i]++;
    pos.push_back(i);
  }
  int count(int i,int d) {
    i+=N;
    ans+=d*a[i];
    return ans;
  }
  void to0() {
    ans=0;
  }
  void clear() {
    for(int p:pos) a[p]=0;
    pos.clear();
    to0();
  }
}fw;

struct Seg{
  vector<pii>segs;
  void add(int l,int r) {
    segs.push_back(pii(l+N,r+N));
  }
  void de(int i) {
    i+=N;
    while(!segs.empty() && segs.back().first<=i){
      int& j=segs.back().first;
      fw.insert(j-N);
      j++;
      if(j>segs.back().second)
        segs.pop_back();
    }
  }
  void clear() {
    segs.clear();
  }
}sg;

void solve() {
  int n,mx=0;
  cin>>n;
  vector<int>a(n+1);
  for(int i=1;i<=n;i++) {
    cin>>a[i];
    mx=max(a[i],mx);
  }
  vector<vector<int>>nums(mx+1);
  for(int i=1;i<=n;i++) nums[a[i]].push_back(i);
  ll ans=0;
  for(int num=0;num<=mx;num++) {
    vector<int>&pos=nums[num];
    if(pos.empty()) continue;
    pos.push_back(n+1);
    int minn=0,now=0;
    fw.clear();
    sg.clear();
    fw.insert(0);
    for(int i=1,j=0;i<=n;) {
      if(a[i]==num) {
        sg.de(now);
        ans+=fw.count(now++,1);
        fw.insert(now);
        j++;
        i++;
      }
      else {
        if(now==minn) {
          int l=now-1,r=now-(pos[j]-i);
          now-=(pos[j]-i);
          fw.to0();
          sg.add(r,l);
          i=pos[j];
        }
        else {
          ans+=fw.count(--now,-1);
          fw.insert(now);
          i++;
        }
        minn=min(minn,now);
      }
    }
  }
  cout<<ans<<'\n';
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int T=1;
  cin>>T;
  while(T--) solve();
  return 0;
}