【洛谷P4867】Gty的二逼妹子序列
阿新 • • 發佈:2020-11-23
題目
題目連結:https://www.luogu.com.cn/problem/P4867
Autumn和Bakser又在研究Gty的妹子序列了!但他們遇到了一個難題。
對於一段妹子們,他們想讓你幫忙求出這之內美麗度\(\in [a,b]\)的妹子的美麗度的種類數。
為了方便,我們規定妹子們的美麗度全都在\([1,n]\)中。
給定一個長度為\(n(1 \le n \le 100000)\)的正整數序列\(s(1 \le si \le n)\),對於\(m(1 \le m \le 1000000)\)次詢問l,r,a,b
,每次輸出\(s_l \cdots s_r\)中,權值\(\in [a,b]\)的權值的種類數。
思路
如果沒有 \(a,b\) 的約束,隨便搞一個莫隊就可以了。
但是有 \(a,b\) 的限制之後,我們統計答案的時候如果暴力列舉 \([a,b]\) 的數值複雜度顯然是 \(O(nm)\),如果用樹狀陣列求區間和的話,莫隊的端點每移動一次都會多出 \(O(\log n)\) 的複雜度,總複雜度是 \(O(n^{\frac{3}{2}}\log n)\),不可接受。
發現統計答案只有 \(O(m)\) 次,所以我們可以考慮一個修改 \(O(1)\),查詢 \(O(\sqrt{n})\) 的做法。值域分塊一下,每次 \([a,b]\) 就轉換為了左右的零散區間和中間的完整區間。
時間複雜度 \(O(m\sqrt{n})\)
程式碼
#include <bits/stdc++.h> using namespace std; const int N=100010,M=1000010; int n,m,T,ans[M],a[N],cnt[N],sum[N],bel[N]; struct node { int l,r,a,b,id; }ask[M]; bool cmp(node x,node y) { return bel[x.l]==bel[y.l] ? x.r<y.r : bel[x.l]<bel[y.l]; } void add(int x) { if (!cnt[x]) sum[bel[x]]++; cnt[x]++; } void del(int x) { cnt[x]--; if (!cnt[x]) sum[bel[x]]--; } int calc(int l,int r) { int s=0; for (int i=l;i<=r;i++) s+=(cnt[i]>0); return s; } int main() { scanf("%d%d",&n,&m); T=sqrt(n)+1; for (int i=1;i<=n;i++) { scanf("%d",&a[i]); bel[i]=(i-1)/T+1; } for (int i=1;i<=m;i++) { scanf("%d%d%d%d",&ask[i].l,&ask[i].r,&ask[i].a,&ask[i].b); ask[i].id=i; } sort(ask+1,ask+1+m,cmp); for (int i=1,l=1,r=0;i<=m;i++) { for (;l>ask[i].l;l--) add(a[l-1]); for (;r>ask[i].r;r--) del(a[r]); for (;r<ask[i].r;r++) add(a[r+1]); for (;l<ask[i].l;l++) del(a[l]); int p=(ask[i].a-1)/T+1,q=(ask[i].b-1)/T+1,id=ask[i].id; if (p==q) ans[id]=calc(ask[i].a,ask[i].b); else { ans[id]=calc(ask[i].a,T*p)+calc(T*(q-1)+1,ask[i].b); for (int j=p+1;j<q;j++) ans[id]+=sum[j]; } } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }