一个数组包含N个正整数,其中有些是重复的。一个前缀后缀集是满足这样条件的下标对(P,S), 0<= P,S < N 满足数组元素A[0..P]的值也在A[S..N - 1]的值中出现,并且A[S..N - 1]中的值也再A[0..P]中出现。换句话说前缀的集合A[0..P]与后缀集合A[S..N - 1]包含完全相同的值。求这样的前缀后缀集合的数量。
例如:3 5 7 3 3 5,共有14个集合符合条件:(1, 4), (1, 3), (2, 2), (2, 1), (2, 0), (3, 2), (3, 1), (3, 0), (4, 2), (4, 1), (4, 0), (5, 2), (5, 1), (5, 0)
本题由
@javaman 翻译。
Input
第1行:一个数N, 表示数组的长度(1 <= N <= 50000)。第2 - N + 1行:每行1个数,对应数组中的元素Ai。(1 <= Ai <= 10^9)
Output
输出符合条件的集合数量。
Input示例
6357335
Output示例
14
思路:正序记录每个位置出现的数字种类数,逆序也记录每个位置的种类数,从大到小枚举种类数,如果种类数减小一,那么判断减去的数字是否相同,不同就跳出。特判1和全部种类数。
PS:数据太弱,不确定是不是正解(刚开始二分查找下标多加了1,竟然只wa一组数据!!!!)
Code:
#include <bits/stdc++.h> #define INF 1e9 using namespace std; const int AX = 5e4+66; int a[AX]; int pre[AX]; int rear[AX]; map<int,int>mp; int main(){ int n ; scanf("%d",&n); for( int i = 0 ; i < n ; i++ ){ scanf("%d",&a[i]); } memset( pre , 0 ,sizeof(pre) ); memset( rear , 0 ,sizeof(rear) ); mp[a[0]] = 1; pre[0] = 1; for( int i = 1 ; i < n ; i ++ ){ if( !mp[a[i]] ){ mp[a[i]] = 1; pre[i] = pre[i-1] + 1; }else{ pre[i] = pre[i-1]; } } mp.clear(); mp[a[n-1]] = 1; rear[0] = 1; for( int i = n - 2 ; i >= 0 ; i -- ){ if( !mp[a[i]] ){ mp[a[i]] = 1; rear[n-1-i] = rear[n-i-2] + 1 ; }else{ rear[n-1-i] = rear[n-i-2]; } } int num = pre[n-1]; int res = 0; int pos1 = lower_bound( pre, pre + n , num )-pre; int pos2 = lower_bound( rear , rear + n , num )-rear; int id1 = pos1; int id2 = pos2; int x1 = n - pos1; int x2 = n - pos2; res += x1 * x2; for( int i = num - 1 ; i >= 2 ; i -- ){ if( a[pos1] == a[n-1-pos2] ){ pos1 = lower_bound( pre , pre + n , i ) - pre; pos2 = lower_bound( rear , rear + n , i ) - rear; res += ( pos1 - id1 ) * ( pos2 - id2 ); id1 = pos1; id2 = pos2; }else break; } pos1 = upper_bound( pre , pre + n , 1 ) - pre; pos2 = upper_bound( rear , rear + n , 1 ) - rear; if( a[0] == a[n-1] ){ res += ( pos1 ) * ( pos2 ); } cout << res << endl; return 0; }