题目链接:https://www.nowcoder.com/acm/contest/139/E
题目描述
Bobo has a sequence of integers s1, s2, ..., sn where 1 ≤ si ≤ k.
Find out the number of distinct sequences modulo (109+7) after removing exactly m elements.
输入描述:
The input consists of several test cases and is terminated by end-of-file. The first line of each test case contains three integers n, m and k. The second line contains n integers s1, s2, ..., sn.
输出描述:
For each test case, print an integer which denotes the result.
示例1
输入
3 2 2 1 2 1 4 2 2 1 2 1 2
输出
2 4
备注:
* 1 ≤ n ≤ 105 * 1 ≤ m ≤ min{n - 1, 10} * 1 ≤ k ≤ 10 * 1 ≤ si ≤ k * The sum of n does not exceed 106.
题意:给你n个数,然后删除m个问你不同的序列有多少种。
做了51nod的那个题才懂这个题如何做,就拿来补了。https://blog.csdn.net/passer__/article/details/81701761
因为对于长度不一,可以删除的数的数量也不一样,所以先预处理下同样数字出现的点,然后对于每个字母出现标记下。
对于当前i点记录和他相同的那个数字上次出现的位置。
考虑对于一个数的话要的是一个子序列,那么有三种情况。
①:前边i-1个数字已经删除了j个了 就是 dp[i-1][j]
②:前边i-1个数字已经删除了j-1个了就是dp[i-1][j-1] 再把这个删除
③:考虑某个点在可删的范围重复了
出现重复的情况 对于某一位重复的话,还需要减去重复的。
比如有1,2,3,4,5,41,2,3,4,5,4
1,2,31,2,3所有的子序列:{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}后面再加一个4的情况{1,4},{2,4},{3,4},{1,2,4},{1,3,4},{2,3,4},{1,2,3,4}
那么这个4到底是从前一个来还是从后一个来喃?两种情况只能选一种,而且从上面阔以得出,重复的就只有这里7种
所以转移方程是:
④需要减去上一次出现的位置-1的那个数的序列。
dp[i][j] - dp[重复点-1][总共应该删除的j - 2个重复之间的数];
代表他们直接的数都删除掉,然后不够的从前边凑出来就好了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
int a[100005];
ll dp[100005][20];
int pre[100005];
int vis[20];
int main()
{
int n,m,k;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
for(int i=0;i<=k;i++)
vis[i]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pre[i]=vis[a[i]];
vis[a[i]]=i;
}
for(int i=0;i<=m;i++)//m个中删除m个
dp[i][i]=1;
for(int i=1;i<=n;i++)
{
dp[i][0]=1;//删除0个的方案数
for(int j=1;j<=min(i-1,m);j++)//最多删除min(i-1,m)
{
dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%mod;//假如没有重复的数
if(pre[i] && i-pre[i]<=j) //如果不是第一次出现而且两次出现的差值在j之内
dp[i][j]=((dp[i][j]-dp[pre[i]-1][j-(i-pre[i])])+mod)%mod;
}
}
printf("%lld\n",dp[n][m]);
}
return 0;
}