Codeforces 145 C Lucky Subsequence

最近在刷DP,刷了好多水题信心满满,然后做了一场figo出的“陈题”,被虐出了翔。

赛后看了figo的题解,又去standing里看了各神犇的各种神写法,又加入了自己c++的c写法,汇成了自己的代码- -。

话说选择了某一些元素就唯一确定了一个子序列,所以子序列中元素的位置与这个子序列是否合法没关系。

所以我们可以吧原序列分成两个子集,一个全是lucky numbers,剩下的全是unlucky numbers。

那么合法的子序列可以这么构成:在lucky里选a个不同的数,在unlucky里选k-a个数。

那么就涉及到dp的范围了,由于lucky numbers会有重复,所以可以看做是一个多重背包的问题。

设有m个不同的lucky number,s[]代表lucky number出现的次数。

那么问题就转换成了,求从m个物品中选出i个物品有多少种取法.

设dp[i][j]代表从i个物品中选出j个物品的组合数。

dp[i][j]=dp[i-1][j](表示不用第i个物品)+dp[i-1][j-1]*s[i]。

自然,最后的结果是sum(dp[m][i]*C(cnt,k-i)),cnt代表unlucky number的个数。

C(n,m)%mod的方法是照神犇抄的,对数论涉及不深啊。。求大神帮忙解释,,感谢感谢。

代码:

#include<cstdio>
#include<iostream>
#include<map>
#define X 100010
#define mod 1000000007
using namespace std;
typedef long long ll;
map<int,int> g;
map<int,int>::iterator gi;
ll f[X]={1},dp[X]={1};
ll mi(ll a,ll b){
	ll as=1;
	while(b){
		if(b&1)as=(as*a)%mod;
		 b>>=1;a =(a *a)%mod;
	}
	return as;
}
ll c(ll a,ll b){
	if(a<b)return 0;
	ll as=f[a];
	as=(as*mi(f[  b],mod-2))%mod;
	as=(as*mi(f[a-b],mod-2))%mod;
	return as;
}
bool luck(int x){
	while(x){
		if(x%10!=4&&x%10!=7)
		    return 0;
		x/=10;
	}
	return 1;
}
int main(){
	int i,j,k,n,m,x,cnt;
	ll as;
	as=cnt=0;
	for(i=1;i<X;i++)
	    f[i]=(f[i-1]*i)%mod;
	scanf("%d%d",&n,&k);
	for(i=1;i<=n;i++){
		scanf("%d",&x);
		if(luck(x))g[x]++;
		else       cnt++;
	}
	m=0;
	for(gi=g.begin();gi!=g.end();gi++){
		m=min(m+1,k);
		for(i=m;i;i--){
		    dp[i]+=dp[i-1]*gi->second;
		    while(dp[i]>=mod)dp[i]-=mod;
		}
	}
	for(i=0;i<=m;i++)
	    as=(as+dp[i]*c(cnt,k-i))%mod;
	printf("%I64d\n",as);
	return 0;
}



 

你可能感兴趣的:(Codeforces 145 C Lucky Subsequence)