【洛谷3396】雜湊衝突(大力分塊)
阿新 • • 發佈:2018-12-09
大致題意:給你一個長度為的陣列以及個操作,操作有兩種:一種是將修改為,另一種操作是求出%。
樸素的暴力
我們先來寫一個樸素的暴力,程式碼如下:
int main()
{
register int i,Q,x,y,ans;register char op;
for(read(n),read(Q),i=1;i<=n;++i) read(a[i]);
while(Q--)
{
read_alpha(op),read(x),read(y);
if (op^'A') a[x]=y;//對於修改操作直接O(1)修改
else
{
for(ans=0,i=y;i<=n;i+=x) ans+=a[i];//據題意統計答案
write(ans),pc('\n');//輸出
}
}
return fwrite(Fout,1,FoutSize,stdout),0;
}
我們可以驚喜地發現,這樣就有分了。如果你寫得足夠優秀,說不定可以直接AC。
預處理答案+暴力更新
我們可以用來記錄詢問時的答案。
首先,預處理出答案,並用大小的陣列把答案存下來,對於詢問,可以輸出答案,並暴力更新。
然後,我們悲劇地發現,還沒等到,直接記憶體炸飛了。。。
程式碼略。
如何優化
我們可以對第二個上天了的程式碼進行優化。
記錄全部答案畢竟所耗記憶體太大了,能不能只記錄一部分的答案,然後另一部分暴力算呢?
就可以考慮分塊。
我們可以先暴力預處理模數時的答案,並用陣列將其儲存下來。
然後,對於修改,我們可以在的時間複雜度內列舉每一個小於等於 的模數,然後更新對應的。
對於詢問,我們分兩種情況討論:
- 如果模數,就直接輸出。
- 如果模數,就暴力求解,且列舉的數的個數肯定小於。
這樣的演算法應該是的。
一個玄學的小優化
雖然我個人認為最優塊的大小應該是,但是,實踐證明,以為塊的大小要比快很多。
程式碼
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<0?-(x):(x))
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
#define N 150000
#define SIZE 400
int FoutSize=0,OutputTop=0;char Fin[Fsize],*FinNow=Fin,*FinEnd=Fin,Fout[Fsize],OutputStack[Fsize];
using namespace std;
int n,Q,blo,a[N+5],ans[SIZE][SIZE];
inline void read(int &x)
{
x=0;static char ch;
while(!isdigit(ch=tc()));
while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
}
inline void read_alpha(char &x)
{
while(!isalpha(x=tc()));
}
inline void write(int x)
{
if(!x) return (void)pc('0');
while(x) OutputStack[++OutputTop]=x%10+48,x/=10;
while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;
}
int main()
{
register int i,j,x,y,res;register char op;
for(read(n),read(Q),blo=pow(n,0.33333),i=1;i<=n;++i) read(a[i]);//讀入資料,這裡的塊的大小取n^(1/3)要比n^(1/2)快很多
for(i=1;i<=n;++i) for(j=1;j<=blo;++j) ans[j][i%j]+=a[i];//O(n^(4/3))暴力預處理出模數≤n^(1/3)時的答案
while(Q--)
{
read_alpha(op),read(x),read(y);
if(op^'A')//對於修改操作
{
for(i=1;i<=blo;++i)//列舉每一個小於等於n^(1/3)的模數,修改對應答案
ans[i][x%i]+=y-a[x];//減去原來的值,加上更新之後的值
a[x]=y;//修改
}
else//對於詢問操作
{
if(x<=blo) {write(ans[x][y]),pc('\n');continue;}//如果模數≤n^(1/3),直接輸出預處理得到的答案
for(i=y,res=0;i<=n;i+=x) res+=a[i];//否則,暴力統計答案
write(res),pc('\n');//輸出答案
}
}
return fwrite(Fout,1,FoutSize,stdout),0;
}