不幸运的序列

【问题描述】
仅包含 4 或 7 的数被称为幸运数。
一个序列的子序列被定义为从序列中删去若干个数,剩下的数组成的新序列。
两个子序列被定义为不同的当且仅当其中的元素在原始序列中的下标的集合不
相等。对于一个长度为N的序列,共有2^N个不同的子序列。 (包含一个空序列)。
一个子序列被称为不幸运的,当且仅当其中不包含两个相同的幸运数。
对于一个给定序列,求其中长度恰好为 K 的不幸运子序列的个数,答案 mod
10^9+7 输出。
【输入格式】
第一行两个正整数 N,K,表示原始序列的长度和题目中的 K。
接下来一行 N 个整数 a i ,表示序列中第 i 个元素的值。
【输出格式】
仅一个数,表示不幸运子序列的个数。(mod 10^9+7)
【样例输入】
3 2
1 1 1
【样例输出】
3
【样例输入】
4 2
4 7 4 7
【样例输出】

4

【样例解释】
对于样例 1,每个长度为 2 的子序列都是符合条件的。
对于样例2,4个不幸运子序列元素下标分别为:{1, 2}, {3, 4}, {1, 4}, {2, 3}。
注意下标集{1, 3}对应的子序列不是“不幸运”的,因为它包含两个相同的幸运数
4.
【数据规模与约定】
56。
70%的数据,1 ≤ 푂 ≤ 10, 푎 푖 ≤ 10000。
对于100%的数据,1 ≤ 푂 ≤ 100000,퐾 ≤ 푂,1 ≤ 푎 푖 ≤ 10 9 。



分析:

如果你试着构造幸运数组,你就会发现其实并不多,那么我们可以直接构造不幸运的序列,设一个不幸运的序列中有x个不幸运的数字,y个幸运数字,则此种的不幸运的序列总数:ans=C(x,tot1)*f[tot2][y](tot1表示不幸运的数字总数,tot2表示幸运的数字总数)

f[i][j]表示前i种幸运数字,选出j种的总数,因为不幸运性,我们对于一个幸运数字要么一个不选0种,要么选一个sum[k](k表示该幸运数字),

则f[i][j]=f[i-1][j]+f[i-1][j-1]*sum[k]

由于数据比较大,又有排列组合,所以加个逆元就可以了。


参考程序:

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int maxn=110000;
const int Mod=1e9+7;
int a[maxn];
LL f[maxn],F[maxn];
int n,m;
int total=0,totun=0;
int sum[maxn];
vector<LL> luck;
void dfs(LL k,int dep){
	if (k)luck.push_back(k);
	if (dep>9)return;
	dfs(k*10+4,dep+1);
	dfs(k*10+7,dep+1);
}
void init(){
	luck.clear();
	dfs(0,1);
	sort(luck.begin(),luck.end());
}
LL exgcd(LL a,LL b,LL &x,LL &y){
	if (!b){
		x=1;y=0;
		return a;
	}
	LL d=exgcd(b,a%b,y,x);
	y-=(a/b)*x;
}
LL ny(LL i){
	LL x,y;
	LL d=exgcd(i,Mod,x,y);
	return (x+Mod)%Mod;
}
void work(LL *f,int n){
	f[0]=1;
	for (int i=0;i<n;i++)
		f[i+1]=f[i]*(n-i)%Mod*ny(i+1)%Mod;
}
int main(){
	freopen("lucky.in","r",stdin);
	freopen("lucky.out","w",stdout);
	scanf("%d%d",&n,&m);
	init();totun=n;
	for (int i=1;i<=n;i++){
		int x;
		scanf("%d",&x);
		int P=lower_bound(luck.begin(),luck.end(),x)-luck.begin()+1;
		if (luck[P-1]==x){
			sum[P]++;
			if (sum[P]==2){
				totun-=2;
				total++;
			}
			if (sum[P]>2)totun--;
		}
	}
	work(F,totun);
	f[0]=1;
    for (int i=1;i<=luck.size();i++)
		if (sum[i]>=2){
	        for (int j=i;j>=1;j--)
				f[j]=(f[j]+f[j-1]*sum[i]%Mod)%Mod;
	    }
	LL res=0;
	for (int i=0;i<=m;i++)
		res+=F[i]*f[m-i]%Mod,res%=Mod;
	printf("%lld",res);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值