Hdu acm 3486 step5.3.6(树状数组)

来源:互联网 发布:澳大利亚旅游 知乎 编辑:程序博客网 时间:2024/04/30 14:16

Interviewe

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)

Total Submission(s): 4389    Accepted Submission(s): 1077

 

 

Problem Description

YaoYao has a company and he wants to employm people recently. Since his company is so famous, there are n people comingfor the interview. However, YaoYao is so busy that he has no time to interviewthem by himself. So he decides to select exact m interviewers for this task.

YaoYao decides to make the interview asfollows. First he queues the interviewees according to their coming order. Thenhe cuts the queue into m segments. The length of each segment is , which meanshe ignores the rest interviewees (poor guys because they comes late). Then,each segment is assigned to an interviewer and the interviewer chooses the bestone from them as the employee.

YaoYao’s idea seems to be wonderful, but hemeets another problem. He values the ability of the ith arrived interviewee asa number from 0 to 1000. Of course, the better one is, the higher ability valueone has. He wants his employees good enough, so the sum of the ability valuesof his employees must exceed his target k (exceed means strictly large than).On the other hand, he wants to employ as less people as possible because of thehigh salary nowadays. Could you help him to find the smallest m?

 

 

Input

The input consists of multiple cases.

In the first line of each case, there aretwo numbers n and k, indicating the number of the original people and the sumof the ability values of employees YaoYao wants to hire (n≤200000, k≤1000000000). In the second line,there are n numbers v1, v2, …, vn (each number is between 0 and 1000), indicating the abilityvalue of each arrived interviewee respectively.

The input ends up with two negativenumbers, which should not be processed as a case.

 

 

Output

For each test case, print only one numberindicating the smallest m you can find. If you can’t find any, output -1instead.

 

 

Sample Input

11 300

7 100 7 101 100 100 9 100 100 110 110

-1 -1

 

 

Sample Output

3

Hint

We need 3 interviewers to help YaoYao. Thefirst one interviews people from 1 to 3, the second interviews people from 4 to6,

and the third interviews people from 7 to9. And the people left will be ignored. And the total value you can get is100+101+100=301>300.

 

题解:

这道题用的是区间最值和二分的方法来算出答案的。

下面是网上某位大神对区间最值的代码:

RMQ(Range Minimum/Maximum Query)问题:

  RMQ问题是求给定区间中的最值问题。当然,最简单的算法是O(n)的,但是对于查询次数很多(设置多大100万次),O(n)的算法效率不够。可以用线段树将算法优化到O(logn)(在线段树中保存线段的最值)。不过,Sparse_Table算法才是最好的:它可以在O(nlogn)的预处理以后实现O(1)的查询效率。下面把SparseTable算法分成预处理和查询两部分来说明(以求最小值为例)。

预处理:

预处理使用DP的思想,f(i, j)表示[i, i+2^j - 1]区间中的最小值,我们可以开辟一个数组专门来保存f(i, j)的值。

例如,f(0, 0)表示[0,0]之间的最小值,就是num[0], f(0, 2)表示[0, 3]之间的最小值, f(2, 4)表示[2, 17]之间的最小值

注意, 因为f(i, j)可以由f(i, j - 1)和f(i+2^(j-1), j-1)导出, 而递推的初值(所有的f(i, 0) =i)都是已知的(在这里,j-1<j所以第一个循环的为列j循环,然后再进行行i的循环)

所以我们可以采用自底向上的算法递推地给出所有符合条件的f(i, j)的值。

 

查询:

假设要查询从m到n这一段的最小值, 那么我们先求出一个最大的k,使得k满足2^k <(n - m + 1).

于是我们就可以把[m, n]分成两个(部分重叠的)长度为2^k的区间: [m, m+2^k-1], [n-2^k+1, n];(2^k < n-m+1  -> m+2^k-1<n,m<n-2^k+1  -> 所以两个区间出现重叠,其最大值就是[m,n]区间的最大值)

而我们之前已经求出了f(m,k)为[m, m+2^k-1]的最小值, f(n-2^k+1, k)为[n-2^k+1, n]的最小值

我们只要返回其中更小的那个,就是我们想要的答案, 这个算法的时间复杂度是O(1)的.

例如, rmq(0, 11)= min(f(0, 3), f(4, 3))

由此我们要注意的是预处理f(i,j)中的j值只需要计算log(n+1)/log(2)即可,而i值我们也只需要计算到n-2^k+1即可。(预处理中,i+2^j-1 = n,i>0;j <log2(n+1);

同时还有,i+2^j -1<= n)

#include <iostream>

#include <string>

#include <math.h>

using namespace std;

 

#define maxs( a , b ) a>b?a:b

#define mins( a , b ) a>b?b:a

const int MAX_N = 50005;

 

int d[MAX_N];

int dpmin[MAX_N][20];

int dpmax[MAX_N][20];

int n;

 

void create_Dpmin(){

    int i , j;

    for( i = 1 ; i <= n ; i++ )

         dpmin[i][0] = d[i];

    for( j = 1 ; j <= log((double)(n+1))/log(2.0) ; j++ ){

         for( i = 1 ; i+(1<<j)-1 <= n ; i++ ){

               dpmin[i][j] = mins(dpmin[i][j-1] , dpmin[i+(1<<(j-1))][j-1] );   

         }

    }    

}

void create_Dpmax(){

    int i , j;

    for( i = 1 ; i <= n ; i++ )

         dpmax[i][0] = d[i];

    for( j = 1 ; j <= log((double)(n+1))/log(2.0) ; j++ ){

         for( i = 1 ; i+(1<<j)-1 <= n ; i++ ){

               dpmax[i][j] = maxs(dpmax[i][j-1] , dpmax[i+(1<<(j-1))][j-1] );   

         }

    }    

}

 

int getmax( int a , int b ){

   int k = (int)(log((double)(b-a+1))/log(2.0));

   return maxs( dpmax[a][k] , dpmax[b-(1<<k)+1][k] );   

}

 

int getmin( int a , int b ){

   int k = (int)(log((double)(b-a+1))/log(2.0));

   return mins( dpmin[a][k] , dpmin[b-(1<<k)+1][k] );   

}

void Init()

{

    create_Dpmin();

    create_Dpmax();    

}

 

int main()

{  

   freopen( "in.txt" , "r" , stdin );

   int i , m , a , b;

   scanf("%d%d",&n,&m);

   for( i = 1 ; i <= n ; i++ ){

          scanf("%d",&d[i]);

    }

   Init();

   while( m-- ){

          scanf("%d%d",&a,&b);    

           printf("%d\n",getmax(a,b)-getmin(a,b)); 

   }  

   return 0;   

}

而如果只用二分和区间最值还是会超时,所以有一个解题的步骤,就是记录如果N/k(N为总人数,k为留下的人数)的队伍人数相同(即当N/k==N/(k+1)每队只留下一个人的话),即sum的值不用改变再加上新来的人的价值。

用G++超时,用c++不超时。

源代码:

#include <iostream>

#include <stdio.h>

#include <math.h>

#include <algorithm>

using namespace std;

#define maxs(a,b) a>b?a:b;

#define MAX 200050

int d[MAX];

int dpmax[MAX][20];

int n,v;

void create_dpmax()

{

   for(int i = 1;i <= n;i++)

     dpmax[i][0] = d[i];

   for(int j = 1;j <= log((double)(n+1))/log(2.0);j++)

   {

     for(int i = 1;i+(1<<j)-1 <= n;i++)

     {

        dpmax[i][j] = maxs(dpmax[i][j-1],dpmax[i+(1<<j-1)][j-1]);

     }

   }

}

 

int getmax(int a,int b)

{

   intk = (int)(log((double)(b-a+1))/log(2.0));

   returnmaxs(dpmax[a][k],dpmax[b-(1<<k)+1][k]);

}

int main()

{

   while(scanf("%d%d",&n,&v) != EOF)

   {

     if(n== -1 && v == -1)

        break;

     for(int i = 1;i <= n;i++)

        scanf("%d",&d[i]);

     create_dpmax();

     intk = 1,flag = 1,m,pre  = -1,ps,pos=0,j,sum= 0;

 

     while(k<= n)

     {

        j = n/k;

        m = j*k;

        if(j== pre)

          pos = ps;

        else

        {

          pos = 0;

          sum = 0;

        }

        while(pos+j<= m)

        {

          sum += getmax(pos+1,pos+j);

          if(sum> v)

          {

             printf("%d\n",k);

             flag = 0;

             break;

          }

          pos += j;

        }

        if(!flag)

          break;

        ps = m;

        pre = j;

        k++;

     }

     if(k> n)

        printf("-1\n");

   }

   return0;

}

 


0 0
原创粉丝点击