「CF86D」Powerful array 解題報告
阿新 • • 發佈:2019-01-18
define 出現 加法 del getchar power 優化 端點 lar 直接上莫隊咯
題面
給出一個\(n\)個數組成的數列\(a\),有\(t\)次詢問,每次詢問為一個\([l,r]\)的區間,求區間內每種數字出現次數的平方×數字的值 的和
思路:
直接上莫隊咯 然後就T了
然後就T了
沒學過莫隊?!我也沒辦法
這道題的數據範圍在\(2e5\)的級別,有人會問莫隊肯定要炸啊 捏~
時限5000ms,那就可以亂搞了
但是!還是要加一些優化
如何優化?
1.對於算法本身的優化
由於莫隊可以說是一個塊狀暴力的算法,就是把區間劃分為\(\sqrt n\)塊然後在塊內暴力(到頭還是暴力)
我們可以把要查詢的區間當做點表示在平面直角坐標系上,RT:
就像這樣把詢問放在平面直角坐標系上,\(\large x\) 軸為詢問該區間的順序,\(\large y\)軸表示該區間的右端點,如果我們忽略區間的\(L\)值的影響,不難看出,雖然我們已經把區間劃分在一個塊裏了,但是還是有很多冗余的操作,如果要是這些冗余的操作少一點就好了,這當然可以啦!
我們可以讓塊內的區間按\(\large R\) 遞增,這樣能省很多時間,但是在區間過渡的時候,我們還是會做很多多余的操作,因為區間都是遞增的,這樣改變塊的時候就可能有一個很大的落差,就掉了下去,可以自己想象一下
為了避免上述的現象,我們可以讓區間像一個波浪一樣,這樣就很高效了,這樣的劃分方式叫做奇偶劃分
應該是這麽叫的吧,還有一個是奇偶性剪枝
inline bool cmp(node a,node b){//代碼是關鍵,講了啥不重要(手動劃線) return (pos[a.l]==pos[b.l])?(pos[a.l]&1)?a.r<b.r:a.r>b.r : a.l<b.l; }//千萬不要寫if,會T!
接著就是塊的大小,同樣影響速度,一般普通的塊的大小應該是\(\large \sqrt n\),但是,根據某奆佬研究,大小為\(\large n^{0.54}\)時更快,Orz
2.對於程序本身優化,說人話就是卡卡常
比如:
加點
register
非遞歸函數前加個
inline
不用快讀用
fread
還有!乘法變加法……
然後就把最大時間卡到了622ms
啊哈哈哈哈哈
Code:
#include<bits/stdc++.h> #define getchar() *(p++)//在快讀基礎上改一點就行了 #define Re register//卡常必備 #define ll long long #define M 1000010 #define N 200010 using namespace std; struct node{ int l,r,i; }b[N];//sum是數的多少,pos表示在哪塊 int a[N],pos[N],n,m,sum[M],l,r,block; ll Ans[N],ans; char bf[1<<25],*p; int read(){ Re int s=0; Re char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { s=(s<<1)+(s<<3)+c-'0'; c=getchar(); } return s; } inline bool cmp(node a,node b){//奇偶劃分 return (pos[a.l]==pos[b.l])?(pos[a.l]&1)?a.r<b.r:a.r>b.r : a.l<b.l; }//註意,這裏不要寫if語句,會T inline void Add(Re ll x) { sum[x]++;//有些人寫在前面,那樣的話就應該是+1 ans+=(sum[x]+sum[x]-1)*x;//就是由原來的乘變成了加,手算一小部分也沒有關系啦 } inline void Del(Re ll x) { ans-=(sum[x]+sum[x]-1)*x;//這裏和上面也是一樣的 sum[x]--;//為什麽我感覺上面-1會更慢呢~ } int main() { Re int i; bf[fread(bf,1,1<<25,stdin)]='\0';p=bf;//fread大法 n=read();m=read();block=pow(n,0.54);//神奇的塊的大小 for(i=1;i<=n;i++) a[i]=read(),pos[i]=i/block; for(i=1;i<=m;i++) b[i].l=read(),b[i].r=read(),b[i].i=i; sort(b+1,b+1+m,cmp);l=1; for(i=1;i<=m;i++)//然後上莫隊 { while(r<b[i].r) Add(a[++r]); while(r>b[i].r) Del(a[r--]); while(l<b[i].l) Del(a[l++]); while(l>b[i].l) Add(a[--l]); Ans[b[i].i]=ans; } for(i=1;i<=m;i++) printf("%lld\n",Ans[i]); return 0; }
再說一遍
最後註意,sort的時候,如果數組本來就有序了,sort會很慢,所以千萬不要在裏面加if了,容易T
「CF86D」Powerful array 解題報告