NOIP模擬21:「Median·Game·Park」
阿新 • • 發佈:2021-07-21
T1:Median
線性篩+桶+隨機化(??什麼鬼?)。
首先,題解一句話秀到了我:
考慮輸入如此詭異,其實可以看作隨機資料
隨機資料??
這就意味著分佈均勻。。
又考慮到w<=k<=n
可以用桶了。
中位數暴力算的話是排序後取中間。
但是時間明顯不允許。只能\(O(n)\)過掉。所以要維護兩個中位數指標(k%2==1當然就是一個了)。
由於資料隨機,分佈均勻,所以可以直接跳桶。
笑死,我當時不信還把資料輸了出去,發現有的相鄰資料差了幾百,就這還能直接跳。。。。。好吧,我膚淺了。。。。
差點沒調出來的程式碼:
200行的煌煌大作QWQ #include<bits/stdc++.h> using namespace std; namespace STD { #define ll long long #define rr register const int N=1.1e7; const int MAXN=1.8e8+3; int n,k; int w; ll cnt,prime[N]; bool notprime[MAXN]; double ans; int s2[N]; int ton[N<<1]; inline void Prime() { for(rr int i=2;i<MAXN;i++) { if(!notprime[i]) prime[++cnt]=i; for(rr int j=1;j<=cnt&&i*prime[j]<MAXN;j++) { notprime[i*prime[j]]=1; if(!(i%prime[j])) break; } } } inline int read() { rr int x_read=0,y_read=1; rr char c_read=getchar(); while(c_read<'0'||c_read>'9') { if(c_read=='-') y_read=-1; c_read=getchar(); } while(c_read<='9'&&c_read>='0') { x_read=(x_read*10)+(c_read^48); c_read=getchar(); } return x_read*y_read; } }; using namespace STD; int main() { Prime(); n=read(),k=read(),w=read(); for(rr int i=1;i<=n;i++) { prime[i]=prime[i]*i%w; s2[i]=prime[i]+prime[i/10+1]; } if(k&1) { int *p=ton+s2[1],l=1,sum=1; ton[s2[1]]++; for(rr int i=2;i<=k;i++) { sum++; ton[s2[i]]++; if(s2[i]<=(p-ton)) l++; if(l<((sum>>1)+1)) while(l<((sum>>1)+1)) { p++; while(!(*p)) p++; l+=(*p); } else while(l-(*p)>=((sum>>1)+1)) { l-=(*p); p--; while(!(*p)) p--; } } ans+=(p-ton); for(rr int i=k+1;i<=n;i++) { ton[s2[i]]++; if(s2[i]<=p-ton) l++; ton[s2[i-k]]--; if(s2[i-k]<=p-ton) l--; if(l<((k>>1)+1)) while(l<((k>>1)+1)) { p++; while(!(*p)) p++; l+=(*p); } else while(l-(*p)>=((k>>1)+1)) { l-=(*p); p--; while(!(*p)) p--; } ans+=(p-ton); } printf("%.1lf\n",ans); } else { int *p1=ton+min(s2[1],s2[2]),*p2=ton+max(s2[1],s2[2]),l2=2,l1=1,sum=2; ton[s2[1]]++; ton[s2[2]]++; for(rr int i=3;i<=k;i++) { sum++; ton[s2[i]]++; if(s2[i]<=(p1-ton)) l1++; if(s2[i]<=(p2-ton)) l2++; if(l2<((sum>>1)+1)) while(l2<((sum>>1)+1)) { p2++; while(!(*p2)) p2++; l2+=(*p2); } else while(l2-(*p2)>=((sum>>1)+1)) { l2-=(*p2); p2--; while(!(*p2)) p2--; } if(l1<(sum>>1)) while(l1<(sum>>1)) { p1++; while(!(*p1)) p1++; l1+=(*p1); } else while(l1-(*p1)>=(sum>>1)) { l1-=(*p1); p1--; while(!(*p1)) p1--; } } double temp=((p1-ton)+(p2-ton)); ans+=(temp/2.00); for(rr int i=k+1;i<=n;i++) { ton[s2[i]]++; if(s2[i]<=(p1-ton)) l1++; if(s2[i]<=(p2-ton)) l2++; ton[s2[i-k]]--; if(s2[i-k]<=p1-ton) l1--; if(s2[i-k]<=p2-ton) l2--; if(l2<((k>>1)+1)) while(l2<((k>>1)+1)) { p2++; while(!(*p2)) p2++; l2+=(*p2); } else while(l2-(*p2)>=((k>>1)+1)) { l2-=(*p2); p2--; while(!(*p2)) p2--; } if(l1<(k>>1)) while(l1<(k>>1)) { p1++; while(!(*p1)) p1++; l1+=(*p1); } else while(l1-(*p1)>=(k>>1)) { l1-=(*p1); p1--; while(!(*p1)) p1--; } temp=(p1-ton)+(p2-ton); ans+=temp/2.00; } printf("%.1lf\n",ans); } }
其他的事情
在討論時土哥提到了一個叫”對頂堆”的東西來維護中位數,當然,\(O(nlogn)\)會TLE。
這個東西其實就是維護兩個堆,一個大根堆,一個小根堆。
小根堆裡的數全部大於大根堆裡的數,這樣就有單調性了,相當於排了個序。
但比直接用排序演算法在N上少了個指數。
當有數進來時,先與兩個堆頂比較如果大於大根堆頂就進小根堆,否則進大根堆。
然後比較兩個堆的大小,然後將多的數放進另一個堆即可。
中位數就是堆頂之和除以2
至於說k%2==1的情況,你就不要把中位數往堆裡放即可。
T2:Game
考場上一眼看出來就是貪心,直接放了個堆上去,還納悶為啥這麼簡單呢。。
然後T了。
正解是\(O(nk)\)
還是桶。一場考試三道題,兩道考桶。。。。
這題有個看起來很明顯但是你往往會忽視的性質:
如果你拿進序列的數比當前序列裡的最大值還要大,那麼它下一輪一會被拿走
簡單到無需證明。。
但是他會決定你是\(A\)還是\(T\)。
記得開longlong。
#include<bits/stdc++.h> using namespace std; namespace STD { #define ll long long #define rr register #define inf INT_MAX const int N=100004; const int K=2004; int n,k,p; int *po; ll score[2]; ll a[N]; int b[N]; int read() { rr int x_read=0,y_read=1; rr char c_read=getchar(); while(c_read<'0'||c_read>'9') { if(c_read=='-') y_read=-1; c_read=getchar(); } while(c_read<='9'&&c_read>='0') { x_read=(x_read*10)+(c_read^48); c_read=getchar(); } return x_read*y_read; } }; using namespace STD; int main() { n=read(),k=read(); for(rr int i=1;i<=n;i++) a[i]=read(); while(k--) { int p=read(); ll temp=-inf; int roun=1; int now=p; for(rr int i=1;i<=p;i++) { temp=max(temp,a[i]); b[a[i]]++; } po=b+temp; temp=-inf; while(roun<=n) { if(temp>(po-b)) { score[roun&1]+=temp; temp=-inf; } else { score[roun&1]+=(po-b); int x=*po; x--; *po=x; } now++; if(now<=n) { if(a[now]>(po-b)) temp=a[now]; else b[a[now]]++; } while(((*po)==0)&&(po>b)) po--; roun++; } printf("%lld\n",score[1]-score[0]); score[0]=score[1]=0; } }
T3:Park
還在推方程,先鴿掉好了。