Spring-2-H Array Diversity(SPOJ AMR11H)解题报告及测试数据

Array Diversity

Time Limit:404MS     Memory Limit:0KB     64bit IO Format:%lld & %llu

 

Description

Here we go! 

Let's define the diversity of a list of numbers to be the difference between the largest and smallest number in the list.

For example, the diversity of the list (1, -1, 2, 7) = 7 - (-1) = 8.

 A substring of a list is considered a non-empty sequence of contiguous numbers from the list. For example, for the list (1,3,7), the substrings are (1), (3), (7), (1,3), (3,7), (1,3,7). A subsequence of a list is defined to be a non-empty sequence of numbers obtained by deleting some elements from the list. For example, for the list (1,3,7), the subsequences are (1), (3), (7), (1,3), (3,7), (1,7), (1,3,7).

 

Given a list of length N find the number of substrings and subsequences in this list with the maximum diversity. If a substring/subsequence having maximum diversity occurs multiple times in the list, each of its occurences adds towards the answer.   And tell Harry Potter your answer

 

Input (STDIN):

The first line contains T, the number of test cases. Then follow T test case blocks.

Each blocks starts with the first line containing the number N.

The second line contains a list of numbers in this list.

 

Output (STDOUT):

For each test case, output the number of substrings and the number of subsequences in this list with the maximum diversity.

Since the answers maybe very large, output them modulo 1000000007.

 

Constraints:

T <= 10

N <= 100,000

Each number in the list is between 1 and 100,000 inclusive.

 

Sample Input:

3

3

1 2 3

4

1 4 3 4

3

3 2 1

 

Sample Output:

1 2

3 6

1 2

题解:

  1. 题目大意是求出包含最大的diversity(最大值和最小值之差 )的子序列和子集个数,  其实就是求包含最大值和最小值的子序列和子集个数。

  2. 思路是先求出最大值和最小值以及其位置,然后计算。需要注意的是,如果最大值等于最小值,需要特判,所有的子序列和非空子集都满足题意,所以子序列个数就是1+2+3+……+n=n*(n+1)/2,非空子集个数就是2^n -1。由于数据比较大,所以求幂运算需要另写函数,类似矩阵的快速幂。

  3. 如果最大值不等于最小值,那么子集显然是从从最小值中至少取出一个(2^k1 -1),最大值中至少取出一个(2^k2 -1),剩下的数中取出任意个(即并上任意子集 ),三者相乘即可。  

  4.  计算子序列,思路是计算包含不同最大值和最小值的子序列个数之和,注意避免重复。避免重复的方法就是从前向后找最大值和最小值的组合对,前面的组合对可以包含后面的,但是计算后面的组合对时,不能包含前面的。               

以下是代码:

#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cctype>
using namespace std;
  
#define ss(x) scanf("%d",&x)
#define ff(i,s,e) for(int i=s;i<e;i++)
#define fe(i,s,e) for(int i=s;i<=e;i++)
#define print(x) printf("%d
",x)
#define write() freopen("1.in","r",stdin)
#define float double
typedef long long LL;
  
const int N = 100010;
const LL Mod = 1000000007;
int n,T,sm[N],la[N];
LL a[N],ma,mi;
LL ans1,ans2;
  
LL ppow(int n){  //2的n次幂
    LL ans = 1,t =2;
    while(n){
        if(n%2)ans = (ans*t)%Mod;
        t = ((t%Mod)*(t%Mod))%Mod;
        n = n>> 1;
    }
    return ans;
}
int main(){
    //write();
    ss(T);
    while(T--){
        ss(n);
        fe(i,1,n)scanf("%lld",&a[i]);
        ma = mi = a[1];
        fe(i,2,n){ //求出最大值和最小值
            if(a[i]>ma)ma=a[i];
            else if(a[i]<mi)mi = a[i];
        }
        if(ma == mi){  //全部数字相同时,特判
            ans1 = n*(n+1)/2 % Mod;  //所有子序列满足
            ans2 = ppow(n)-1;   //所有非空子集均满足
        }else{
            int k1=0,k2=0;
            fe(i,1,n){  //找出最大值和最小值的位置和个数
                if(a[i]==mi)sm[++k1]=i;
                if(a[i]==ma)la[++k2]=i;
            }
            int i=1,j=1,pre=1;
            ans1=0;
            while(i<=k1 && j <=k2){  //算出满足题意的子序列和子集
                int t1 = min(sm[i],la[j]);
                int t2 = max(sm[i],la[j]);
                ans1 = (ans1+(LL)(t1-pre+1)*(n-t2+1))%Mod;
                sm[i]<la[j]?i++ : j++;
                pre = min(t1+1,min(sm[i],la[j]));
            }
            ans2 = ((ppow(k1)-1)*(ppow(k2)-1)%Mod * ppow(n-k1-k2))%Mod;
        }
        printf("%lld %lld
",ans1,ans2);
    }
}

  

以下是可测试数据:

sample input:

2

7

1 2 7 5 3 7 1

6

1 7 7 7 1 1

sample output:

10 72

11 49

 
原文地址:https://www.cnblogs.com/gzdaijie/p/4317012.html