hihocoder 1609 数组分拆II

来源:互联网 发布:php无限极分类 编辑:程序博客网 时间:2024/05/22 16:50

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

给定一个包含N个整数的数组A=[A1, A2, ... AN]。小Ho想将A拆分成若干连续的子数组,使得每个子数组中的整数都是两两不同的。

在满足以上条件的前提下,小Ho想知道子数组数量最少是多少。

同时他还想知道,在数量最少的前提下有多少中不同的拆法。

例如对于[1, 2, 3, 1, 2, 1],最少需要3个子数组。有5种不同的拆法:

[1], [2, 3, 1], [2, 1]

[1, 2], [3, 1], [2, 1]

[1, 2], [3, 1, 2], [1]

[1, 2, 3], [1], [2, 1]

[1, 2, 3], [1, 2], [1]
输入

第一行包含一个整数N。

第二行包含N个整数A1, A2, ... AN。

对于30%的数据,满足1 ≤ N ≤ 10

对于60%的数据,满足1 ≤ N ≤ 1000

对于100%的数据,满足1 ≤ N ≤ 100000, 1 ≤ Ai ≤ 100000
输出

输出两个整数。第一个表示子数组最少的数量,第二个表示在数量最少的前提下有多少种拆法。

由于拆法可能非常多,你只需要输出答案除以1000000007的余数。
样例输入

6
1 2 3 1 2 1

样例输出

3 5


分析:从左往右扫描, 找出最少的分组情况,然后DP 设 d[i] 为 (以最少分组情况考虑 )1~i 能有几种分法。d[i] = Sum(d[j] 满足条件的j).

#include<iostream>#include<stdio.h>#include<algorithm>#include<string.h>#include<vector>const int maxn = 1000000;const int inf = 1e9;const long long int mod = 1000000007;typedef long long ll;using namespace std;int n,a[maxn+5],L[maxn+5],R[maxn+5],last[maxn+5];int l[maxn+5],r[maxn+5],cnt;ll d[maxn+5];int main(){    int MaxL,s,t;    ll cot;    while(~scanf("%d",&n))    {        for(int i=1; i<=n; i++) scanf("%d",&a[i]);        memset(last,-1,sizeof(last));//找出每一个数 上一次出现的 下标L[i]        for(int i=1; i<=n; i++) L[i] = last[a[i]], last[a[i]] = i;        memset(last,-1,sizeof(last));// 找出每一个数 下一次出现的 下标R[i]        for(int i=n; i; i--) R[i] = last[a[i]],last[a[i]] = i;        for(int i=1; i<=n; i++) if(R[i]<0) R[i] = n+1;        cnt = 0, s = 1, t = 1,MaxL = n+2;        for(int i=1; i<=n+1; i++)// 找出最少的分组。 以最少分组的情况考虑。有一些数是必须组合在一起的,设哪些区间为l[i],r[i]        {            if(i==MaxL)            {                l[++cnt] = s, r[cnt] = max(t,s) ,s=t=i , MaxL=R[i];            }            else            {                if(MaxL>R[i]) t = i,MaxL = R[i];            }        }        memset(d,0,sizeof(d));        r[cnt] = n;        d[0] = 1, l[cnt+1] = r[cnt+1] = n+1;// d[i]表示 1~i 以最少分组的情况考虑 能有分几出种。        for(int i=1; i<=cnt; i++)//(l[i-1],r[i-1]) x1 x2 x3... (l[i],r[i]) y1 y2 y3 ... (l[i+1],r[i+1])./ d[yi] = sum(d[xj] 满足条件的).        {            s = r[i-1], cot = 0, MaxL = -1;            for(int j=l[i];j<=r[i];j++) s = max(s,L[j]);//在 (l[i],r[i])中找出 开始的 最左符合边界。            for(int j=s; j<l[i]; j++) cot = (cot + d[j])%mod;            for(int j=r[i]; j<l[i+1]; j++)            {                MaxL = max(MaxL,L[j]);// 随着 j 的右移 最左符合边界 或者不变或者上升 不会下降的。                while(s<MaxL&&s<l[i]) cot = (cot - d[s]+mod)%mod, s++;                d[j] = cot;            }        }        printf("%d %lld\n",cnt,d[n]);    }    return 0;}/*72 2 5 4 4 3 34 1142 4 1 5 6 7 8 1 9 10 11 12 1 133 251411 11 11 13 2 3 5 2 1 12 8 7 5 114 1*/



原创粉丝点击