codeforces 739E - Gosha is hunting
這道題有三種做法,感受一下:
感覺到了歪果仁費盡腦汁想出來的神仙貪心腦洞題被中國人套路算法踩爆的凄涼。。。(我的名字是不是暴露了我的真實實力)
===================================================================================
首先先要明白:有A個A球,B個B球,用了一個A球貢獻為ai,B球貢獻為bi,兩個都用貢獻為1-(1-ai)(1-bi)=ai+bi-ai*bi
?
先講講最無腦的費用流吧。。。
顯然st先分別流向A球和B球流量為球的個數,費用為0
兩種球分別連向所有小精靈,流量為1,費用為貢獻
對于小精靈,連向ed分別建一條流量為1費用為0,流量為1費用為-ai*bi的邊,這樣如果被兩種球分別流了,可以減掉多算的
復雜度O(EK)E約等于2n,K是A+B所以大概是O(n^2)帶個大常數的,真菜,還是正解有意思
?
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std;struct node {int x,y,c,next;double d; }a[410000];int len,last[4100]; void ins(int x,int y,int c,double d) {len++;a[len].x=x;a[len].y=y;a[len].c=c;a[len].d=d;a[len].next=last[x];last[x]=len;len++;a[len].x=y;a[len].y=x;a[len].c=0;a[len].d=-d;a[len].next=last[y];last[y]=len; }int st,ed; int pre[4100],c[4100]; double d[4100],ans; int list[4100];bool v[4100]; bool spfa() {memset(d,-63,sizeof(d));d[st]=0;c[st]=(1<<30);memset(v,false,sizeof(v));v[st]=true;int head=1,tail=2;list[1]=st;while(head!=tail){int x=list[head];for(int k=last[x];k;k=a[k].next){int y=a[k].y;if(a[k].c>0&&d[y]<d[x]+a[k].d&&(!(d[x]+a[k].d-d[y]<=1e-10))){d[y]=d[x]+a[k].d;c[y]=min(a[k].c,c[x]);pre[y]=k;if(v[y]==false){v[y]=true;list[tail]=y;tail++;if(tail==4050)tail=1;}}}v[x]=false;head++;if(head==4050)head=1;}if(fabs(d[ed]-d[0])<=1e-10)return false;else{int y=ed;ans+=c[ed]*d[ed];while(y!=st){int k=pre[y];a[k].c-=c[ed];a[k^1].c+=c[ed];y=a[k].x;}return true;} }double A[4100],B[4100]; int main() {int n,m,aa,bb;scanf("%d%d%d",&n,&aa,&bb);for(int i=1;i<=n;i++)scanf("%lf",&A[i]);for(int i=1;i<=n;i++)scanf("%lf",&B[i]);len=1;st=n+1,ed=n+2;ins(st,n+3,aa,0),ins(st,n+4,bb,0);aa=n+3,bb=n+4;for(int i=1;i<=n;i++){ins(aa,i,1,A[i]);ins(bb,i,1,B[i]);ins(i,ed,1,0);ins(i,ed,1,-A[i]*B[i]);}while(spfa());printf("%.6lf\n",ans);return 0; } 費用流?
------------------------------------------------------------------------------
這是瑟瑟發抖的官方欽定貪心。。。
大前提:A球和B球都要完全用完,用得越多期望一定越大
考慮先按B的貢獻大到小給小精靈排序,然后枚舉最后一個B球用的位置i(這個位置必定不會超過A+B),這樣右邊的只有可能是不用或用a(然并卵)
這只是構建了一個前提,使得1~i之中必定每個小精靈都用了球,如果中間沒有選A球替代B球,B球一定是貪心選擇前B個的
我們先假設1~i之中全部都只用了B球,再考慮A球的放法
對于兩個球都放而言,對于B球用的個數沒有影響,對于貢獻的影響為a-a*b
而B球只有B個卻放了i個,意味著要用i-B次單獨選擇A球來替換(注意是剛好i-B次),對于貢獻的影響為a-b
考慮對于當前1~i再次排序貪心,如果我們把雙選放前面,單A放后面,使得這樣的選擇方案是最優的,我們用鄰項交換的方式做一下:
設二次排序后x<y,x小精靈用兩個球,y小精靈用A球,則有ax+bx-ax*bx+ay>ay+by-ay*by+ax 即?bx-ax*bx>by-ay*by
所以我們按照b*(1-a)大到小排序,這樣就把兩種選擇方式分成兩段了
然而我們還是不知道斷點在哪里,所以我們需要枚舉斷點j
于是:要在j~i中選擇前i-B個貢獻最大的(貢獻是a-b),以及在1~j-1中(貢獻是a-a*b)和i+1~n中(貢獻為a)找到A-(i-B)個最大的
這是一個求全局前k大的數的和的問題,可以用數據結構解決
我%的是CQzhangyu(全網唯一寫這個的)%到吐于是乎順便跟著也用了對頂堆其實是復制粘貼splay各種操作拼起來不知道哪里出鍋了,你也可以寫個平衡樹來d飛我啊~~~~
復雜度O(n^2logn)跑得可真快呢像我的常數一樣快,真棒!
?
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> using namespace std; const double eps=1e-10;struct heap {priority_queue<double>a,b;void del(){while(!b.empty()&&fabs(a.top()-b.top())<=eps)a.pop(),b.pop();}double top(){del();return a.top();}void pop(){del();a.pop();}int size(){return a.size()-b.size();}void push (double x){a.push(x);}void erase(double x){b.push(x);}void clear(){while(!a.empty())a.pop();while(!b.empty())b.pop();} }; double sum; struct bst {heap A,B; int lim;void insert(double x){A.push(-x),sum+=x;if(A.size()>lim)B.push(-A.top()),sum+=A.top(),A.pop();}void del(double x){if(B.size()>0&&x<=B.top())B.erase(x);else{sum-=x,A.erase(-x);if(A.size()<lim&&B.size()>0)A.push(-B.top()),sum+=B.top(),B.pop();}}void clear(){A.clear(),B.clear();} }H[2]; void init(int l1,int l2) {H[0].clear(),H[0].lim=l1;H[1].clear(),H[1].lim=l2;sum=0; }//---------------------------------findkth----------------------------------------------------struct node{double a,b;}p[2100]; bool cmp(node n1,node n2){return n1.b>n2.b;} bool cmd(node n1,node n2){return n1.b*(1-n1.a)>n2.b*(1-n2.a);} int main() {int n,A,B;scanf("%d%d%d",&n,&A,&B);for(int i=1;i<=n;i++)scanf("%lf",&p[i].a);for(int i=1;i<=n;i++)scanf("%lf",&p[i].b);sort(p+1,p+n+1,cmp);double ss=0;for(int i=1;i<B;i++)ss+=p[i].b;double ans=0; int li=min(n,A+B); for(int i=B;i<=li;i++){ss+=p[i].b; sort(p+1,p+i+1,cmd);init(i-B,A-(i-B));for(int j= 1 ;j<=i;j++)H[0].insert(p[j].a-p[j].b);for(int j=i+1;j<=n;j++)H[1].insert(p[j].a);ans=max(ans,ss+sum);for(int j=1;H[0].lim<=i-j;j++){H[0].del(p[j].a-p[j].b);H[1].insert(p[j].a-p[j].a*p[j].b);ans=max(ans,ss+sum);}}printf("%.6lf\n",ans);return 0; } 真慘?
----------------------------------------------------------------------
終于進入到了我做這道題的真正目的:練wqs二分
無腦的dp方程:f[i][j][k]表示枚舉到第i個小精靈,用了j個A球k個B球
記得我之前講了啥:A球和B球都要完全用完,用得越多期望一定越大
前面一句聯想到wqs二分套路固定選擇k個,后面一句說明它的函數值是單調增的,同時用你聰明(???)的腦子想想就知道它是一個上凸包,因為先選的時候肯定是貪心選最大的(注意到這個要求的是最大值,所以二分的值是要減掉的(以前沒見過))
因為有兩維,我們可以wqs套wqs
你問DP轉移?這還用腦子嗎?
復雜度O(nlog^2n)也太無聊了,雖然和正解一樣又有log又有^2,可是^2在log后面真丑~
?
轉載于:https://www.cnblogs.com/AKCqhzdy/p/10248541.html
總結
以上是生活随笔為你收集整理的codeforces 739E - Gosha is hunting的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用python来操作redis用法详解
- 下一篇: P2286 [HNOI2004]宠物收养