zoukankan      html  css  js  c++  java
  • CodeForces 1344D Résumé Review

    题意

    给定一个长度为 (n) 的序列 (a) 和一个整数 (k),构造一个序列 (b) 使得满足以下条件:

    • (0leq b_ileq a_i)

    • (sumlimits_{i=1}^{n}b_i=k)

    最大化 (sumlimits_{i=1}^{n}a_ib_i-b_i^3)

    ( exttt{Data Range:}nleq 10^5,kleq 10^{14})

    题解

    神仙题。

    考虑先设 (b_i)(0),相当于能操作 (k) 次,每次能把某个 (b_i+1)

    考虑设 (f(i,x)=a_ix-x^3),那么我们有

    [Delta f(i,x)=a_i-3x^2+3x-1 ]

    注意到这东西在整数域上是单调递减的,于是可以按照求最大函数值的那个套路来,但是时间复杂度是 (O(klog n)) 的,无法通过。

    注意到我们取出来的最大函数值值是单调不升的,所以可以考虑二分一下最后一次操作对答案的贡献是什么。对于当前考虑的值我们可以通过二分来解一下某个 (i) 至少需要操作几次才能大于等于这个最大增量,最后 check 一下 (sum b_i) 就好了。

    然后由于最大函数值单调不升而不是单调递减,所以外面的二分最好不要二分到一个确切的值,而是二分到一个长度为 (2) 的区间,然后在 check 两个端点。

    这样子可能有些时候操作次数还有剩余,于是就可以最后调整一下值就差不多了。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long int ll;
    const ll MAXN=2e5+51,inf=1e18;
    ll n,kk,l,r,mid,sm;
    ll u[MAXN],v[MAXN];
    inline ll read()
    {
        register ll num=0,neg=1;
        register char ch=getchar();
        while(!isdigit(ch)&&ch!='-')
        {
            ch=getchar();
        }
        if(ch=='-')
        {
            neg=-1;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            num=(num<<3)+(num<<1)+(ch-'0');
            ch=getchar();
        }
        return num*neg;
    }
    inline ll f(ll u,ll x)
    {
        return u==x?inf:u-3*x*x+3*x-1;
    }
    inline ll calc(ll x,ll lim)
    {
        ll l=1,r=u[x],mid,res=u[x];
        while(l<=r)
        {
            mid=(l+r)>>1;
            f(u[x],mid)<=lim?r=mid-1,res=mid:l=mid+1;
        }
        return res;
    }
    inline ll check(ll mid)
    {
        sm=0;
        for(register int i=1;i<=n;i++)
        {
            sm+=(v[i]=calc(i,mid));
        }
        return sm<kk;
    }
    int main()
    {
        n=read(),kk=read();
        for(register int i=1;i<=n;i++)
        {
            u[i]=read(),l=min(l,f(u[i],u[i]-1)),r=max(r,f(u[i],0));
        }
        while(r-l>=2)
        {
            mid=(l+r)>>1;
            check(mid)?r=mid:l=mid;
        }
        r=check(l)?l:r,check(r),kk-=sm;
        for(register int i=1;i<=n;i++)
        {
            kk&&v[i]<u[i]&&f(u[i],v[i])==r?v[i]++,kk--:1;
        }
        for(register int i=1;i<=n;i++)
        {
            printf("%lld ",v[i]);
        }
    }
    
  • 相关阅读:
    155. 最小栈
    160. 相交链表
    PAT 1057 Stack
    PAT 1026 Table Tennis
    PAT 1017 Queueing at Bank
    PAT 1014 Waiting in Line
    PAT 1029 Median
    PAT 1016 Phone Bills
    PAT 1010 Radix
    PAT 1122 Hamiltonian Cycle
  • 原文地址:https://www.cnblogs.com/Karry5307/p/13731799.html
Copyright © 2011-2022 走看看