zoukankan      html  css  js  c++  java
  • P2827 [NOIP2016 提高组] 蚯蚓

    P2827 [NOIP2016 提高组] 蚯蚓

    P2827|ac133

    85 pts

    看到题的思路就是这里每次取最大值,可以使用优先队列大根堆维护每次的最大值,还有一个操作就是每次队列中除去最大元素,其他元素都要加上一个(q),那么这里就是关键的两个操作就是 最大值 (+) 区间加法。区间加法:线段树,但是不行,这里的优先队列没有迭代器,无法返回区间的左右端点。考虑一下其他元素的加 (q),实际上是划出来的两个数减 (q),这样其实相对大小其实没有改变。每次取出的最大值虽然值不对,但是一定是和原操作一样是最大的。每次取出的值和应该取出的值满足一定的关系,对于(x),当第(i) 秒被分出来,将(x - i * q)加入优先队列中,下次第(j)秒时取到这个数((cnt = x - i * q))时,这时将(cnt + (j - 1) * q),这样就是等价于在(i sim j)这段时间内所一共需要加的(q)的总数。
    具体看代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define re register
    const int N = 1e5 + 5;
    const int M = 8e6;
    ll a[N];
    //
    priority_queue<ll> qx;
    ll ansv[M];
    ll ans[M];
    int ttv = 0,tts = 0;
    
    inline int read()
    {
    	re int x=0,f=1;re char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x*f;
    }
    int main()
    {
        ll n,m,q,u,v,t;
        cin >> n >> m >> q >> u >> v >> t;
        for(int i= 1;i <= n;i++)
        {
            a[i] = read();
            qx.push(a[i]);
        }
        double _ = (double)u / v;
        for(int i = 1;i <= m;i++)
        {
            ll vl = (ll)qx.top();
            qx.pop();
            vl += (ll)(i - 1) * q;
            ansv[++ttv] = vl;
            ll px = (ll)vl * _; 
            ll x_px = vl - px;
            px -= (ll)i * q;
            x_px -= (ll)i * q;
            qx.push(px);
            qx.push(x_px);
        }
        while(qx.size())
        {
            ll x = qx.top();
            x += m * q;
            ans[++tts] = x;
            qx.pop();
        }
        for(int i =t;i <= ttv;i += t)
        {
           printf("%lld ",ansv[i]);
        }
        puts("");
        for(int i =t;i <= tts;i+=t)
        {
           printf("%lld ",ans[i]);
        }
        return 0;
    }
    

    100 pts

    这里如果使用优先队列的话,复杂度不行。所以想办法优化,考虑上面的关键字:优先队列 (+) 区间加法优化,区间加法优化已经是最优的情况了,所以在这里需要将优先队列的(log)优化成(O(n)).
    那么就是需要解决选择每次取出的最大值的问题。现在考虑维护三个队列:原来的数组:(q[0])(px)的数组(q[1]),(x-px)的数组(q[2]).这里其实利用了一个单调的性质,这三个队列都是单调递减的序列。

    • 初始化(q[0])队列,先使队列单调递减。
    • 然后先从(q[0])队列将最大元素(maxn\_value)取出,作为切分对象,这里的(maxn\_value)也是剩下的数中最大的,然后计算第一次的(px,x-px),插入到(q[1],q[2])中,等到下一次需要计算最大值时,只可能在 (q[0],q[1],q[2])的队头,因为:现在的(q[0])的队头是第一次操作的最小值,如果没有切分操作那么现在的队头就是最大值,而现在(maxn\_value)被划分成两个数,那么这两个数有可能是最大值的候选项。以此类推。
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    queue<int> qx[4];
    const int N = 1e5 + 5;
    const int M = 8e6;
    ll a[N];
    ll ansv[M];
    ll ans[M];
    int ttv = 0,tts = 0;
    ll solve()
    {
       //返回最大值
       ll ans = -9223372036854775808;
       //找到需要pop的队列
       int maxn = 0;
       //循环三个队列,在队头找到最大值
       for(int i= 0;i < 3;i++){
           //保证队列有数
           if(qx[i].size())
           {
               //
               ll _ = qx[i].front();
               if(_ > ans)
               {
                   ans = _;
                   maxn = i;
               }
           }
       }
       qx[maxn].pop();
       return ans; 
    }
    bool cmp(int a,int b)
    {
        return a > b;
    }
    int main()
    {
        ll n,m,q,u,v,t;
        cin >> n >> m >> q >> u >> v >> t;
        for(int i =1;i <= n;i++)
        {
            scanf("%lld",&a[i]);
        }
        sort(a + 1,a + n + 1,cmp);
        for(int i =1; i<= n;i++)
          qx[0].push(a[i]);
        double x = (double)u / v;
        for(ll i= 1;i <= m;i++)
        {
           ll vl = solve();
           vl += (ll)(i - 1) * q;
           ansv[++ttv] = vl;
           ll px = (ll) vl * x;
           ll x_px = vl - px;
           px -= (ll)i * q;
           x_px -= (ll)i * q;       
           qx[1].push(px);
           qx[2].push(x_px);
        }   
        for(int i =0; i< 3;i++)
        {
            while(qx[i].size())
            {
                ll _ = qx[i].front();
                _ += (ll)m * q;
                ans[++tts] = _;
                qx[i].pop();
            }
        }
        sort(ans + 1,ans + tts + 1,cmp);
        for(int i =t;i <= ttv;i += t)
        {
           printf("%lld ",ansv[i]);
        }
        puts("");
        for(int i =t;i <= tts;i+=t)
        {
           printf("%lld ",ans[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    每日记载内容总结33
    华为机试-尼科彻斯定理
    华为机试-求最大连续bit数
    华为机试-合法IP
    华为机试-票数统计
    华为机试-等差数列
    华为机试-自守数
    中序表达式转后序表式式
    华为机考 给你一个N*M的矩阵,每个位置的值是0或1,求一个面积最大的子矩阵,这个矩阵必须是一个正方形,且里面只能由1构成,输出最大的正方形边长。其中n,m<=400;
    华为机试-求解立方根
  • 原文地址:https://www.cnblogs.com/strategist-614/p/14427159.html
Copyright © 2011-2022 走看看