hdu7020 Array(2021杭電暑假多校5)多數投票演算法
阿新 • • 發佈:2021-08-04
題意
給一個長度為\(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)\)
最後考慮到當前字首和的變化一定是\(+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;
}