UVALive 5990 Array Diversity (容斥原理)
题目
题意
给一个长度为n的序列,问同时包含最大值和最小值的子串和子序列的个数各是多少
解法
-
当序列的最大值和最小值相同时,即序列有同一个数组成,那么
- 子串的个数为:\(n*(n+1)/2\)
- 子序列的个数:\(2^n-1\)
-
最大值和最小值不同时,
- 对于子串,用pos1表示最后遇到的最小值的位置,pos2表示最后遇到的最大值的位置,每一个位置i对于ans1的贡献为\(min(pos1, pos2)\)
- 对于子序列,根据容斥原理,同时包含最大值和最小值的子序列的个数 = 总的子序列的个数 - 不包含最大值的子序列个数 - 不包含最小值的子序列个数 + 既不包含最大值也不包含最小值的子序列的个数,即
\[ans2=(sum[n]-1)-(sum[n-maxnum]-1)-(sum[n-minnum]-1)+(sum[n-maxnum-minnum]-1) \]
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100100;
const long long MOD = 1e9 + 7;
const long long INF = 0x3f3f3f3f;
long long a[N], sum[N];
void init() {
sum[0] = 1;
for(int i = 1; i < N; i++)
sum[i] = (sum[i - 1] * 2) % MOD;
}
int main() {
int T, n;
init();
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
long long _max = 0, _min = INF;
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
_max = max(_max, a[i]);
_min = min(_min, a[i]);
}
if(_max == _min) {
printf("%lld %lld\n", (n * (n + 1) / 2) % MOD, sum[n] - 1);
continue;
}
int maxnum = 0, minnum = 0;
long long ans1 = 0, ans2 = 0;
int pos1 = 0, pos2 = 0;
for(int i = 1; i <= n; i++) {
if(a[i] == _max) {
maxnum ++;
pos1 = i;
}
if(a[i] == _min) {
minnum ++;
pos2 = i;
}
ans1 = (ans1 + min(pos1, pos2)) % MOD;
}
ans2 = ((((sum[n] - 1) - (sum[n - maxnum] - 1) + MOD) % MOD - sum[n - minnum] + MOD) % MOD + sum[n - maxnum - minnum]) % MOD;
printf("%lld %lld\n", ans1, ans2);
}
return 0;
}