BZOJ.3920.Yuuna的禮物(莫隊 分塊套分塊)
阿新 • • 發佈:2018-02-20
return 時間 log solution print typedef mark opera 邊界 插入
但是權值分塊的大小是嚴格值域的,即出現次數為i的值都可能出現在i塊裏,這樣空間無法承受
於是需要分段離散化,對於塊i,用出現次數\(>=i\)的\(A_x\)對其離散化。
\(∑tm_i=n\),所以空間是\(O(n)\)的;時間復雜度\(O(m*sqrt(n))\)(證明見上博客 )
題目鏈接
詳細題解:https://www.cnblogs.com/autsky-jadek/p/4376091.html
代碼參考自:https://www.cnblogs.com/Sakits/p/8445534.html
思路好理解,然而就是寫了一下午+一晚上。。
\(Description\)
給定一個長為n的序列,每次查詢區間中出現次數k1小的數裏面的k2小的數。卡空間。
\(Solution\)
將出現次數按權值分塊,這樣可以實現\(O(1)\)插入,\(O(sqrt(n))\)查詢第k1小的出現次數
但是還需要知道第k2小的值,可以每個塊維護點的個數棵平衡樹,但這樣插入復雜度會變高
同樣可以在每個塊內每個節點再套一個權值分塊,同樣能夠\(O(1)\)
但是權值分塊的大小是嚴格值域的,即出現次數為i的值都可能出現在i塊裏,這樣空間無法承受
於是需要分段離散化,對於塊i,用出現次數\(>=i\)的\(A_x\)對其離散化。
\(∑tm_i=n\),所以空間是\(O(n)\)的;時間復雜度\(O(m*sqrt(n))\)(證明見上博客 )
//細節。。
#include <cmath>
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#define gc() getchar()
#define pb push_back
typedef unsigned short ushort;
const int N=40005;
ushort n,size,m,A[N],B[N],bel[N],Ans[N];
int sz1[N],sum1[N],tm[N];//都可能有負
std::vector<ushort> sum2[N]/*某塊中存在某數*/,ref[N],rank[N]/*某個數在某塊中的位置*/,sz2[N];
struct Ask
{
int l,r,k1,k2,id;
bool operator <(const Ask &a)const
{
return bel[l]==bel[a.l]? r<a.r : bel[l]<bel[a.l];
// return bel[l]==bel[a.l]? ((l-1)/size&1 ? r>a.r : r<a.r) : bel[l]<bel[a.l];
}
}q[N];
inline ushort read()
{
ushort now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
int Get_Pos1(int p)//找到k1所在大塊、and 具體的套著的那個塊
{
int x=1;
for(int i=1; i<=bel[n]; ++i)
if((p-=sz1[i])<=0) {p+=sz1[x=i]; break;}
for(int tmp=std::min((int)n,x*size-1),i=(x-1)*size; i<=tmp; ++i)
if(!(p-=(sum1[i]>0))) return i;
}
int Get_Pos2(int id,int p)
{
int x=1;
for(int i=1; i<sum2[id].size(); ++i)//不能是<bel[sum2[id].size()],不如直接<size,反正它肯定會結束
if((p-=sz2[id][i])<=0) {p+=sz2[id][x=i]; break;}
for(int tmp=std::min((int)sum2[id].size(),x*size),i=(x-1)*size; i<tmp; ++i)//註意vector的邊界
if(!(p-=sum2[id][i])) return i;
}
int Query(int k1,int k2)
{
int id=Get_Pos1(k1);
return ref[id][Get_Pos2(id,k2)];
}
inline void Update(int p,int delta)//僅在tm[p]>0(不是if(tm[p]))時加!
{//修改塊內的塊
sum1[tm[p]]+=delta;//同理 這個也要減掉
if(sum1[tm[p]] && delta==1) ++sz1[bel[tm[p]]];
if(!sum1[tm[p]] && delta==-1) --sz1[bel[tm[p]]];
int pos=rank[p][tm[p]-1];//對於相同數多次出現已是位於不同的塊,so 出現次數就不用管了
sz2[tm[p]][bel[pos]]+=delta, sum2[tm[p]][pos]+=delta;
}
inline void Modify(int p,int delta)
{
if(tm[p]>0) Update(p,-1);//要把原先的次數刪掉
tm[p]+=delta;
if(tm[p]>0) Update(p,1);
}
//inline void Add(int p)//WA: 沒有把之前的sz1減掉
//{
// if(tm[p]>0) Update(p,-1);//要把原先的次數刪掉
// ++tm[p];
// if(++sum1[tm[p]]==1) ++sz1[bel[tm[p]]];//對於外面的塊 每個出現次數只計算一次
// if(tm[p]>0) Update(p,1);
//}
//inline void Subd(int p)
//{
// if(tm[p]>0) Update(p,-1);
// if(!--sum1[tm[p]]) --sz1[bel[tm[p]]];
// if(--tm[p]>0) Update(p,1);//次數-1後的加上
//}
int main()
{
n=read(), size=sqrt(n);
bel[0]=1;//得有bel[0]給vector使
for(int i=1; i<=n; ++i) A[i]=B[i]=read(),++tm[A[i]],bel[i]=i/size+1;//全從0開始分塊更方便
std::sort(B+1,B+1+n);
for(int i=1; i<=n; ++i)//Discrete
{
for(int j=1; j<=tm[B[i]]; ++j)
sum2[j].pb(0), rank[B[i]].pb(ref[j].size()), ref[j].pb(B[i]);
tm[B[i]]=0;//去重
}
for(int i=1; i<=n; ++i)
for(int j=0; j<=bel[sum2[i].size()]; ++j) sz2[i].pb(0);//<=!
m=read();
for(int i=1; i<=m; ++i)
q[i].l=read(),q[i].r=read(),q[i].k1=read(),q[i].k2=read(),q[i].id=i;
std::sort(q+1,q+1+m);
for(int l=1,r=0,i=1; i<=m; ++i)
{
while(l<q[i].l) Modify(A[l++],-1);
while(l>q[i].l) Modify(A[--l],1);
while(r<q[i].r) Modify(A[++r],1);
while(r>q[i].r) Modify(A[r--],-1);
Ans[q[i].id]=Query(q[i].k1,q[i].k2);
}
for(int i=1; i<=m; ++i) printf("%d\n",Ans[i]);
return 0;
}
BZOJ.3920.Yuuna的禮物(莫隊 分塊套分塊)