【CF626G】Raffles(貪心)
阿新 • • 發佈:2020-12-02
- 有\(n\)個獎池,第\(i\)個獎池獎金為\(p_i\),已有票數為\(l_i\)。
- 你有\(m\)張彩票可以把它們分配到獎池中,第\(i\)個獎池中分到的票數\(s_i\)需滿足\(s_i\le l_i\),且有\(\frac{s_i}{s_i+l_i}\)的概率獲得全部獎金\(p_i\)。
- \(q\)次修改,每次將一個獎池中的票數增/減\(1\),求修改後獲得獎金的最大期望。
- \(n,m,q\le2\times10^5,p_i,l_i\le10^3\)
一個經典的貪心問題
這應該是一個比較經典的貪心問題。
顯然\(\frac{s_i}{s_i+l_i}\times p_i\)隨著\(s_i\)
但現在還有修改。
看似暴力的修改操作
首先注意下幾個細節,例如若\(l_i\)增大且原先\(m\)張彩票沒有用完則直接使用,若\(l_i\)減小且原先\(s_i\)等於\(l_i\)則必須從中取出一張彩票。
然後就考慮能否從某個獎池中取出彩票給當前獎池,或取出當前獎池的彩票給另外某個獎池,來增大答案。
要實現這個就需要開兩個\(set\)
那麼要取彩票肯定取減少一張彩票變化最小的獎池,要給彩票肯定給增加一張彩票變化最大的獎池。
至於複雜度,本來我以為這樣暴力做是\(O(ql_ilogn)\)的,然而據說每次修改最多引起一張獎票變化,所以是\(O((n+m+q)logn)\)?
程式碼:\(O((n+m+q)logn)\)
#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define N 200000 #define DB double #define eps 1e-12 using namespace std; int n,m,p[N+5],l[N+5],s[N+5];DB ans; struct Data { int id;DB x;I Data(CI i=0,Con DB& a=1):id(i),x(a){} I bool operator < (Con Data& o) Con {return fabs(x-o.x)>eps?x<o.x:id<o.id;} };set<Data> S[2]; #define Calc(i,s) (1.0*p[i]*(s)/(l[i]+(s)))//計算第i個獎池中放入s張彩票時的期望 #define A(i) Calc(i,s[i]+1)-Calc(i,s[i])//加一張彩票的獎金期望值變化 #define D(i) Calc(i,s[i])-Calc(i,s[i]-1)//減一張彩票的獎金期望值變化 I void Add()//加一張彩票 { RI k=(--S[1].end())->id;ans+=(--S[1].end())->x, S[1].erase(--S[1].end()),s[k]&&(S[0].erase(Data(k,D(k))),0),//刪去原本資訊 ++s[k]^l[k]&&(S[1].insert(Data(k,A(k))),0),S[0].insert(Data(k,D(k)));//更新 } I void Del()//減一張彩票 { RI k=S[0].begin()->id;ans-=S[0].begin()->x; S[0].erase(S[0].begin()),s[k]^l[k]&&(S[1].erase(Data(k,A(k))),0),//刪去原本資訊 --s[k]&&(S[0].insert(Data(k,D(k))),0),S[1].insert(Data(k,A(k)));//更新 } int main() { RI Qt,i,t=0,op,x,y;for(scanf("%d%d%d",&n,&m,&Qt),i=1;i<=n;++i) scanf("%d",p+i); for(i=1;i<=n;++i) scanf("%d",l+i),S[1].insert(Data(i,A(i)));//初始化set W(t^m&&S[1].size()) Add(),++t;W(Qt--&&~scanf("%d%d",&op,&x)) { s[x]^l[x]&&(S[1].erase(Data(x,A(x))),0),s[x]&&(S[0].erase(Data(x,D(x))),0);//刪去原本資訊 if(ans-=Calc(x,s[x]),op==1) {if(++l[x],t^m) {++s[x],ans+=Calc(x,s[x]),++t;goto End;}}//如果彩票沒用完 else if(--l[x],s[x]>l[x]) {--s[x],ans+=Calc(x,s[x]),S[1].size()?(Add(),0):--t;goto End;}//如果不得不拿出彩票 ans+=Calc(x,s[x]);W(s[x]&&S[1].size()&&D(x)<(--S[1].end())->x) ans-=D(x),--s[x],Add();//給別的獎池彩票 W(s[x]^l[x]&&S[0].size()&&A(x)>S[0].begin()->x) ans+=A(x),++s[x],Del();//取別的獎池彩票 End:s[x]^l[x]&&(S[1].insert(Data(x,A(x))),0),s[x]&&(S[0].insert(Data(x,D(x))),0);//更新資訊 printf("%.15lf\n",ans); }return 0; }