时间限制: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;
}
/*
7
2 2 5 4 4 3 3
4 1
14
2 4 1 5 6 7 8 1 9 10 11 12 1 13
3 25
14
11 11 11 13 2 3 5 2 1 12 8 7 5 11
4 1
*/
9064

被折叠的 条评论
为什么被折叠?



