題解 P5048 【[Ynoi2019模擬賽]Yuno loves sqrt technology III】
阿新 • • 發佈:2020-08-06
未經允許,禁止轉載
原文連結
其實本蒟蒻自己想了一會後便有了一些思路,但實現方面還是還是學習了\(\text{dllxl}\)的做法的
強制線上導致一些離線做法沒有辦法解決(如莫隊),我們考慮分塊,預處理出第l到第r個塊的眾數次數,再把零散塊的數一個一個更新答案。
我們用一個vector存某個值的下標序列,設當前答案是ans,如果當前點在左邊,它的後ans+1個元素在r內,則更新答案,右邊則反之。
然而今天我是來教大家卡常的
關於這個題的提交記錄真的卡的我心態爆炸
好像交了一次題解都沒過? 不要封我
好了,正文開始,首先了解一下這個火車頭
這是個好東西,可以讓你\(n^2\)過百萬(霧
但加了這個好像還是T完了?
沒事,我們還有IO優化
下面是我竊了機房大佬的IO優化
namespace quick { #define tp template<typename Type> namespace in { inline char getc() { static char buf[1<<21],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++; } inline int read(char *s) { *s=getc(); while(isspace(*s)) {*s=getc();if(*s==EOF) return 0;} while(!isspace(*s)&&*s!=EOF) {s++;*s=getc();} *s='\0'; return 1; } tp inline int read(Type &x) { x=0;bool k=false;char c=getc(); while(!isdigit(c)) {k|=(c=='-');c=getc();if(c==EOF) return 0;} while(isdigit(c)) {x=(x<<1)+(x<<3)+(c^48);c=getc();} x*=(k?-1:1); return 1; } template <typename Type,typename... Args> inline int read(Type &t,Args &...args) { int res=0; res+=read(t);res+=read(args...); return res; } } using in::read; namespace out { char buf[1<<21];int p1=-1;const int p2=(1<<21)-1; inline void flush() { fwrite(buf,1,p1+1,stdout); p1=-1; } inline void putc(const char &c) { if(p1==p2) flush(); buf[++p1]=c; } inline void write(char *s) { while(*s!='\0') putc(*s),s++; } tp inline void write(Type x) { static char buf[30];int p=-1; if(x<0) {putc('-');x=-x;} if(x==0) putc('0'); else for(;x;x/=10) buf[++p]=x%10+48; for(;p!=-1;p--) putc(buf[p]); } inline void write(char c) {putc(c);} template <typename Type,typename... Args> inline void write(Type t,Args ...args) { write(t);write(args...); } } using out::write; using out::flush; tp inline Type max(const Type a,const Type b) { if(a<b) return b; return a; } tp inline Type min(const Type a,const Type b) { if(a<b) return a; return b; } tp inline void swap(Type &a,Type &b) { a^=b^=a^=b; } tp inline Type abs(const Type a) { return a>=0?a:-a; } #undef tp } using namespace quick;
直接read,write就ok了
還有一個很關鍵很關鍵的優化(敲黑板
這是我們在計算第i個塊到第j個塊的程式碼,\(\text{kmx[][]}\)就是那個陣列,tot是塊總數。
for(int i=1;i<=tot;++i) { for(int j=i;j<=tot;++j) { kmx[i][j]=kmx[i][j-1]; for(int k=kn[j-1]+1;k<=kn[j];++k) kmx[i][j]=max(kmx[i][j],++vis[al[k]]); } memset(vis,0,sizeof(vis)); }
但我們可以發現第一個塊與最後一個塊根本用不上,所以可以改成
for(int i=2;i<tot;++i)
{
for(int j=i;j<tot;++j)
{
kmx[i][j]=kmx[i][j-1];
for(int k=kn[j-1]+1;k<=kn[j];++k)
kmx[i][j]=max(kmx[i][j],++vis[al[k]]);
}
memset(vis,0,sizeof(vis));
}
大約可以快0.1s。
還有要用memset,它絕對比其他方式都快(至少這個題是的)。
還有max不要用STL,自己手寫要快一點。
還有lxl是真的dio
完整程式碼
inline int max(const int &a,const int &b)
{
return a>b?a:b;
}
int main()
{
int t,pre=0;
read(n,t);
for(int i=1;i<=n;++i)
read(a[i]),b[i]=a[i];
tot=(n-1)/mxn+1;
for(int i=1;i<=tot;++i)
kn[i]=kn[i-1]+tot;
kn[tot]=n;
// tot=(n-1)/mxn+1;
// for(int i=1;i<=tot;++i)
// kn[i]=kn[i-1]+mxn;
// kn[tot]=n;
for(int i=1;i<=tot;++i)
for(int j=kn[i-1]+1;j<=kn[i];++j)
bel[j]=i;
sort(b+1,b+n+1);
m=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;++i)
al[i]=lower_bound(b+1,b+m+1,a[i])-b;
for(int i=2;i<tot;++i)
{
for(int j=i;j<tot;++j)
{
kmx[i][j]=kmx[i][j-1];
for(int k=kn[j-1]+1;k<=kn[j];++k)
kmx[i][j]=max(kmx[i][j],++vis[al[k]]);
}
memset(vis,0,sizeof(vis));
}
for(int i=1;i<=n;++i)
{
pos[al[i]].push_back(i);
vps[i]=pos[al[i]].size()-1;
}
int l,r;
while(t--)
{
read(l,r);
l^=pre;r^=pre;
int pl=bel[l],pr=bel[r];
if(pl==pr)
{
int nowans=0;
for(int i=l;i<=r;++i)
nowans=max(nowans,++vis[al[i]]);
for(int i=l;i<=r;++i)
vis[al[i]]=0;
write(nowans,'\n');
pre=nowans;
}
else
{
int nowans=kmx[pl+1][pr-1];
for(int i=l;i<=kn[pl];++i)
{
int siz=pos[al[i]].size();
while(vps[i]+nowans<siz&&pos[al[i]][vps[i]+nowans]<=r) ++nowans;
}
for(int i=kn[pr-1]+1;i<=r;++i)
while(vps[i]-nowans>=0&&pos[al[i]][vps[i]-nowans]>=l) ++nowans;
write(nowans,'\n');
pre=nowans;
}
}
return 0;
}
後記
首先我的程式碼不一定在全天候都能過(但你凌晨交是肯定能過),另外如果你認真讀了我的程式碼,你就會發現我用的是固定塊長mxn=708,但我在初始化塊的時候是這麼寫的
tot=(n-1)/mxn+1;
for(int i=1;i<=tot;++i)
kn[i]=kn[i-1]+tot;
kn[tot]=n;
kn是塊的右端點,實際上應是
tot=(n-1)/mxn+1;
for(int i=1;i<=tot;++i)
kn[i]=kn[i-1]+mxn;
kn[tot]=n;
但上面的寫法竟然AC了,而且比下面的快,這也算玄學卡常嗎
希望有大佬能解釋
完結撒花!