题意:
给出一个长度为n且元素不超过k的序列,问删除m个数之后不同的序列有几种,模。其中,,。
思路:
。
有转移。
考虑到序列里面可能有重复数字,比如
1,3,2,4,1,5,这时如果1,3,2,4,1,5和1,3,2,4,1,5这两种情况应该是同一种方案,所以当前后有两个数字相同,其中一个删除,中间的也全部删除,这是会有重复方案的。
所以对于一个前面有相同数字的i,假设前一个数字为pre[i],相当于要减去pre[i]-1时候删除的数字为j-(i-pre[i]),就相当于当前i没有删除,将pre[i]~i-1全部删除,这种方案是重复的,所以要减去。
dp的初始化在循环中进行会快很多,memset比较慢。
代码:
typedef long long ll;
int cur[15];
int pre[100010];//上一个相同数字出现在哪里
ll dp[100010][15];
int main()
{
int n,m,k;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
int x;
memset(pre,-1,sizeof(pre));
memset(cur,-1,sizeof(cur));
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
if(cur[x]!=-1)pre[i]=cur[x];
cur[x]=i;
}
if(m>n){printf("0\n");continue;}
/*for(int i=1;i<=n;i++)
printf("%d ",pre[i]);
printf("\n");*/
//memset(dp,0,sizeof(dp));
dp[0][0]=1;
dp[1][0]=1;dp[1][1]=1;
for(int i=2;i<=n;i++)
for(int j=0;j<=min(m,i);j++)
{
dp[i][j]=0;
if(j-1>=0&&j-1<=min(m,i-1))dp[i][j]=(dp[i][j]+dp[i-1][j-1])%MOD;
if(j<=min(m,i-1))dp[i][j]=(dp[i][j]+dp[i-1][j])%MOD;
if(pre[i]!=-1&&j-(i-pre[i])>=0&&j-(i-pre[i])<=min(m,pre[i]-1))
dp[i][j]=(dp[i][j]-dp[pre[i]-1][j-(i-pre[i])]+MOD)%MOD;
}
/*for(int i=1;i<=n;i++)
{
for(int j=0;j<=min(m,i);j++)
printf("%lld ",dp[i][j]);
printf("\n");
}*/
printf("%lld\n",dp[n][m]);
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!