士兵杀敌(三)
-
描述
-
南将军统率着N个士兵,士兵分别编号为1~N,南将军经常爱拿某一段编号内杀敌数最高的人与杀敌数最低的人进行比较,计算出两个人的杀敌数差值,用这种方法一方面能鼓舞杀敌数高的人,另一方面也算是批评杀敌数低的人,起到了很好的效果。
所以,南将军经常问军师小工第i号士兵到第j号士兵中,杀敌数最高的人与杀敌数最低的人之间军功差值是多少。
现在,请你写一个程序,帮小工回答南将军每次的询问吧。
注意,南将军可能询问很多次。
-
输入
- 只有一组测试数据
第一行是两个整数N,Q,其中N表示士兵的总数。Q表示南将军询问的次数。(1<N<=100000,1<Q<=1000000)
随后的一行有N个整数Vi(0<=Vi<100000000),分别表示每个人的杀敌数。
再之后的Q行,每行有两个正正数m,n,表示南将军询问的是第m号士兵到第n号士兵。
输出 - 对于每次询问,输出第m号士兵到第n号士兵之间所有士兵杀敌数的最大值与最小值的差。
样例输入:
5 2
1 2 6 9 3
1 2
2 4
样例输
1
7
本题中是要求范围最小值,最大值即rmp,在实践中最常用的是tarjan的Sparse-Table算法,预处理时间是O(nlogn),但是查询时间只需要O(1)。
首先是预处理:dp_min[i][j],dp_max[i][j]分别存放数组中从i开始到2^j的一段的最小值最大值。
开始初始化dp_min[i][0],dp_max[i][0]等于a[i];初始条件,dp状态都有了,写出动态方程式:
dp_min[i][j] = min(dp_min[i][j-1],dp_min[i+(1<<(j-1))][j-1]);
dp_max[i][j] = max(dp_max[i][j-1],dp_max[i+(1<<(j-1))][j-1]);
即可利用动态规划求解。
例如: 1 2 6 9 3
在运算时首先运算1 2,2 6,6 9,9 3,两个数中的最小/大值
再分别计算1 2 6 9,2 6 9 3,四个数中的最小/大值
查询:在返回语句 :min(dp_min[l][k],dp_min[h-(1<<k)+1][k]);
max(dp_max[l][k],dp_max[h-(1<<k)+1][k]);
中是有交叉的,但是不影响结果的正确性。
#include<stdio.h> #include<string.h> #include<math.h> #define max(a,b) a>b?a:b #define min(a,b) a>b?b:a #define cl(arr,val) memset(arr,val,sizeof(arr)); int n,m,low,high,a[100005],dp_min[100005][20],dp_max[100005][20]; void rmq(){ int temp = (int)(log(n)/log(2)); for(int j=1;j<=temp;j++) for(int i=0;i<n;i++){ if(i+(1<<j)<=n){ dp_min[i][j] = min(dp_min[i][j-1],dp_min[i+(1<<(j-1))][j-1]); dp_max[i][j] = max(dp_max[i][j-1],dp_max[i+(1<<(j-1))][j-1]); } } } int rmq_min(int l,int h){ int k = (int)(log(h-l+1)/log(2)); return min(dp_min[l][k],dp_min[h-(1<<k)+1][k]); } int rmq_max(int l,int h){ int k = (int)(log(h-l+1)/log(2)); return max(dp_max[l][k],dp_max[h-(1<<k)+1][k]); } int main(){ int x,y,min,max; scanf("%d%d",&n,&m); cl(a,0); cl(dp_min,0); cl(dp_max,0); for(int i=0;i<n;i++){ scanf("%d",&a[i]); dp_min[i][0]=a[i]; dp_max[i][0]=a[i]; } rmq(); while(m--){ scanf("%d%d",&low,&high); min = rmq_min(low-1,high-1); max = rmq_max(low-1,high-1); printf("%d\n",max-min); } return 0; }
- 只有一组测试数据