#417 Div2 Problem C Sagheer and Nubian Market (二分 && std::accumulate)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#417 Div2 Problem C Sagheer and Nubian Market (二分 && std::accumulate)相关的知识,希望对你有一定的参考价值。

题意 : 给你 n 件物品和你拥有的钱 S, 接下来给出这 n 件物品的价格, 这些物品的价值不是固定不变的, 价格的变化公式是 a[i]+k*i (i代表第 i 件物品, k 代表你选择买的物品数量, a[i]为物品的底价), 现问你最多能够买多少件物品和所买物品总和, 输出时应该使得所买物品总和尽量小

 

分析 : 如果我当前能买 k 件物品, 那我肯定能买数量小于 k 的物品, 如果我当前买不起 k 件物品, 那我肯定也不能买比 k 件要多的物品。所以可以考虑二分解法, 在1~n之间二分查找 k, 这里需要注意的是, 在二分的过程中应该需要对当前的价格进行更新和排序, 才能保证最后输出的物品总和尽量小!

 

技巧 : 这里有需要计算数组前 k 个的和, 可以考虑使用 std::accumulate(begin, end, base), 代表从数组的arr[begin]加到arr[end]的和再加上base, 也就是在base的基础上求arr数组的begin~end的和, 这里有详细介绍(值得一提的是复杂度是 O(n) ) http://classfoo.com/ccby/article/Y749fK

举例 : 

技术分享
#include <vector>
#include <numeric>
#include <functional>
#include <iostream>

using namespace std;

int main( ) 
{

   vector <int> v1, v2( 20 );
   vector <int>::iterator Iter1, Iter2;

   int i;
   for ( i = 1 ; i < 21 ; i++ )
   {
      v1.push_back( i );
   }

   cout << "最初向量v1中个元素的值为:\n ( " ;
   for ( Iter1 = v1.begin( ) ; Iter1 != v1.end( ) ; Iter1++ )
      cout << *Iter1 << " ";
   cout << ")." << endl;

   // accumulate函数的第一个功能,求和
   int total;
   total = accumulate ( v1.begin ( ) , v1.end ( ) , 0 );

   cout << "整数从1到20的和为: " 
        << total << "." << endl;

   // 构造一个前n项和的向量
   int j = 0, partotal;
   for ( Iter1 = v1.begin( ) + 1; Iter1 != v1.end( ) + 1 ; Iter1++ )
   {
      partotal = accumulate ( v1.begin ( ) , Iter1 , 0 );
      v2 [ j ] = partotal;
      j++;
   }

   cout << "前n项和分别为:\n ( " ;
   for ( Iter2 = v2.begin( ) ; Iter2 != v2.end( ) ; Iter2++ )
      cout << *Iter2 << " ";
   cout << ")." << endl << endl;

   // accumulate函数的第二个功能,计算连乘积
   vector <int> v3, v4( 10 );
   vector <int>::iterator Iter3, Iter4;

   int s;
   for ( s = 1 ; s < 11 ; s++ )
   {
      v3.push_back( s );
   }

   cout << "向量v3的初始值分别为:\n ( " ;
   for ( Iter3 = v3.begin( ) ; Iter3 != v3.end( ) ; Iter3++ )
      cout << *Iter3 << " ";
   cout << ")." << endl;

   int ptotal;
   ptotal = accumulate ( v3.begin ( ) , v3.end ( ) , 1 , multiplies<int>( ) );

   cout << "整数1到10的连乘积为: " 
        << ptotal << "." << endl;

   // 构造一个前n项积的向量
   int k = 0, ppartotal;
   for ( Iter3 = v3.begin( ) + 1; Iter3 != v3.end( ) + 1 ; Iter3++ ) {
      ppartotal = accumulate ( v3.begin ( ) , Iter3 , 1 , multiplies<int>( ) );
      v4 [ k ] = ppartotal;
      k++;
   }

   cout << "前n项积分别为:\n ( " ;
   for ( Iter4 = v4.begin( ) ; Iter4 != v4.end( ) ; Iter4++ )
      cout << *Iter4 << " ";
   cout << ")." << endl;
}
View Code

瞎想 : 一开始是在想是否能用背包做, 事实证明在S那个数据量下是不可能的, 而且背包的话, 这里要求的是总和尽量小。

技术分享
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 1e5+10;
LL arr[maxn], change[maxn];
LL n, s;
LL cal(LL k)
{
    for(int i=1; i<=n; i++) change[i] = i*k+arr[i];
    sort(change+1, change+1+n);
    LL ans = accumulate(change+1, change+1+k, 0LL);
    return ans;
}
int main(void)
{
    scanf("%lld %lld", &n, &s);
    for(int i=1; i<=n; i++)
        scanf("%lld", &arr[i]);
    LL L = 1, R = n, mid;
    while(L<=R){
        mid = L + ((R-L)>>1);
        LL sum = cal(mid);
        if(sum <= s) L = mid + 1;
        else R = mid - 1;
    }
    if(L==1){
        puts("0 0");
        return 0;
    }
    L--;
    printf("%lld %lld\n", L, cal(L));
    return 0;
}
View Code

 

以上是关于#417 Div2 Problem C Sagheer and Nubian Market (二分 && std::accumulate)的主要内容,如果未能解决你的问题,请参考以下文章

#417(div2) C. Sagheer and Nubian Market

#417(div2) B - Sagheer, the Hausmeister

#452 Div2 Problem C Dividing the numbers ( 思维 || 构造 )

#381 Div2 Problem C Alyona and mex (思维 && 构造)

#415 Div2 Problem C Do you want a data? (math && 前后缀和 && 快速幂)

div2 .c