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
" , dp[n - 1] , (ans[n - 1] + mod) % mod);
    return 0;
}
原文地址:https://www.cnblogs.com/TnT2333333/p/7677192.html