题意:对于一个数列A,substring是一个连续子串,subsequence是其非连续子序列。对于一个数字序列,记它的diversity是它的最大元素减去最小元素的差。给出一个数字序列,求与它diversity相同的substring和subsequence各有多少个。比如若A为1,4,3,4,则答案为3和6。
解法:组合数学问题。首先,记A中最大元素为ma,最小元素为mi,且最大元素有s1个,最小元素有s2个,数列A长度为n。
则A的所有subsequence共有2^n个,不含ma的subsequence有2^(n-s1)个,不含mi的subsequence有2^(n-s2)个,mi和ma都不含的有2^(n-s1-s2)个,由容斥原理,所求为2^n - 2^(n-s1) - 2^(n-s2) + 2^(n-s1-s2)个。
对于substring的个数,选取的的substring为A[begin] 到 A[end],枚举end从0到n-1,并且用t1记录目前位置最后出现的mi的位置,t2记录目前最后出现的ma的位置,则对于每个枚举的end,ans += min(t1, t2)。详见代码。
比赛的时候没有想清楚,substring个数的求法写得好复杂。。。后来看了男神代码自己默默模仿了一份。。。。
tag:组合数学,counting
1 /* 2 * Author: Plumrain 3 * Created Time: 2013-12-03 19:43 4 * File Name: math-LA-5990.cpp 5 */ 6 #include <iostream> 7 #include <cstdio> 8 #include <algorithm> 9 10 using namespace std; 11 12 typedef long long int64; 13 const int64 mod = 1000000007; 14 const int N = 100005; 15 16 int64 g[N], a[N]; 17 int64 min(int64 a, int64 b) {return a > b ? b : a;} 18 int64 max(int64 a, int64 b) {return a > b ? a : b;} 19 20 int main() 21 { 22 g[0] = 1; 23 for (int i = 1; i < N; ++ i) 24 g[i] = g[i-1] * 2 % mod; 25 26 int T, n; 27 scanf ("%d", &T); 28 while (T--){ 29 int64 mi = mod, ma = 1; 30 scanf ("%d", &n); 31 for (int i = 0; i < n; ++ i){ 32 scanf ("%lld", &a[i]); 33 mi = min(mi, a[i]); 34 ma = max(ma, a[i]); 35 } 36 37 int64 ans1 = 0, ans2 = 0; 38 39 if (mi == ma){ 40 ans1 = ((int64)(n + 1) * n / 2) % mod; 41 ans2 = g[n] - 1; 42 printf ("%lld %lld ", ans1, ans2); 43 continue; 44 } 45 46 int t1 = -1, t2 = -1, s1 = 0, s2 = 0; 47 for (int i = 0; i < n; ++ i){ 48 if (a[i] == mi){ 49 ++ s1; t1 = i; 50 } 51 if (a[i] == ma){ 52 ++ s2; t2 = i; 53 } 54 55 ans1 = (ans1 + min(t1+1, t2+1)) % mod; 56 } 57 58 ans2 = (g[n] - g[n-s1] - g[n-s2] + g[n-s1-s2]) % mod; 59 if (ans2 < 0) ans2 += mod; 60 printf ("%lld %lld ", ans1, ans2); 61 } 62 return 0; 63 }