hihocoder #1609 : 数组分拆II(思维)
题目链接:http://hihocoder.com/problemset/problem/1609
题解:就先拿一个数组最多分成两部分来说吧
8
1 2 3 4 5 1 2 3
显然 输出时2 3
可以这样分(1 2 3( 4 5 )1 2 3)显然分要分在(^4^5^)“^”这3个地方也就是说如果设pre[i]表示i位置最前面能到哪个位置,dp[i]表示i这个位置最少能分成几组。
那么ans[i]=Sum(ans[j] , (dp[j]=dp[i]-1 && j > pre[i]))那么就可以考虑前缀和。如果是单纯的求i的前缀没什么意义,可以考虑球sum[dp[i]]表示到i的位置长度为dp[i]
的一共有几种分法。这样就可以方便求出Sum(ans[j] , (dp[j]=dp[i]-1 && j > pre[i]))
#include <iostream> #include <cstring> #include <cstdio> #define mod 1000000007 using namespace std; const int M = 1e5 + 10; typedef long long ll; int a[M] , pre[M] , dp[M]; ll ans[M] , sum[M]; bool vis[M]; int main() { int n; scanf("%d" , &n); for(int i = 0 ; i < n ; i++) { scanf("%d" , &a[i]); } memset(pre , -1 , sizeof(pre)); memset(dp , -1 , sizeof(dp)); memset(vis , false , sizeof(vis)); int left = -1; for(int i = 0 ; i < n ; i++) { if(vis[a[i]]) { left++; while(a[i] != a[left]) { vis[a[left]] = false; left++; } } pre[i] = left; vis[a[i]] = true; } for(int i = 0 ; i < n ; i++) { if(pre[i] == -1) { dp[i] = 1; } else { if(dp[i] == -1) dp[i] = dp[pre[i]] + 1; else dp[i] = min(dp[i] , dp[pre[i]] + 1); } } for(int i = 0 ; i < n ; i++) { if(pre[i] == -1) { ans[i] = 1; sum[dp[i]] += ans[i]; sum[dp[i]] %= mod; } else { for(int j = max(0 , pre[i - 1]) ; j < pre[i] ; j++) { sum[dp[j]] -= ans[j]; sum[dp[j]] %= mod; } ans[i] += sum[dp[i] - 1]; sum[dp[i]] += ans[i]; sum[dp[i]] %= mod; } } printf("%d %lld\n" , dp[n - 1] , (ans[n - 1] + mod) % mod); return 0; }