其他分享
首页 > 其他分享> > 题解 P5308 [COCI2019] Quiz

题解 P5308 [COCI2019] Quiz

作者:互联网

\(\text{WQS}\) 二分+斜率优化

若没有 \(k\) 的限制,可以用 \(f_i\) 表示剩 \(i\) 个人时获得的最大收益,那么当下一轮剩 \(j\) 个人时,这一轮就淘汰了 \(i-j\) 个人,获得 \(\dfrac{i-j}{i}\) 的奖金。

所以转移方程为 \(f_i=\max\{f_j+\dfrac{i-j}{i}\}(j<i)\)。

这个式子显然可以斜率优化,转化为 \(y=kx+b\) 的形式,即 \(\underline{\huge f_j}_y=\underline{\huge\dfrac{1}{i}}_k\times\underline{\huge j}_x+\underline{\huge f_i-1}_b\)。

现在考虑有 \(k\) 的限制,“恰好 \(k\) 个”显然是 \(\text{WQS}\) 二分的特征,其凸性读者可以自证。我只会打表找规律

另外,此题稍卡精度,所以我加了个时间限制。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const double eps=1e-20;
int n,K;
double f[N];
int q[N],cnt[N];
inline double Y(int i,int j){return f[i]-f[j];}
inline double X(int i,int j){return i-j;} 
inline double slope(int i,int j){return Y(i,j)/X(i,j);}
bool check(double k){
    int l=1,r=1;q[1]=0;
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=n;i++){
        while(l<r&&slope(q[l+1],q[l])>1.0/(double)i+eps)l++;
        int j=q[l];
        f[i]=f[j]+(double)(i-j)/(double)i-k;
        cnt[i]=cnt[j]+1;
        while(l<r&&slope(q[r],q[r-1])+eps<slope(i,q[r]))r--;
        q[++r]=i;
    }return cnt[n]>=K;
}
int main(){
    scanf("%d%d",&n,&K);
    double l=0,r=1e6;
    while(l+eps<r&&(double)clock()/CLOCKS_PER_SEC<=1.9){
        double mid=(l+r)/2.0;
        if(check(mid))l=mid;
        else r=mid;
    }printf("%.9lf\n",f[n]+l*K);
    return 0;
}

标签:huge,cnt,return,COCI2019,int,题解,Quiz,double,underline
来源: https://www.cnblogs.com/aday526/p/solution-p5308.html