[NOIP模拟赛]酷子集
来源:互联网 发布:php mysql 存储过程 编辑:程序博客网 时间:2024/05/23 01:13
题目描述
给出整数N, 则集合S包含整数1, 2, 3, ... , N。考虑S的某个非空子集T,把子集T的所有元素都写下来,如果使用0-9中每个数字的次数都没有超过1次(允许是0次),则把子集T称为酷子集。例如,子集{12,345,67890} 和 {47,109}都是酷子集,而 {147,342}不是,因为数字4使用了2次。同理,{404}也不是酷子集。
输入格式
第1行:1个整数N(1≤N≤10^9),表示集合S的元素个数。
输出格式
第1行:1个整数,表示答案,答案模1e9+7。
输入样例
10
输出样例
767
样例说明
N=10的所有非空子集有2^10-1=1023个。不是酷子集的情况是同时包含1和10,这类子集一共有2^8 =256个,所以答案是1023-256=767。
题解 by zjx
对于所有满足条件的集合,每个数字最多出现一次,而总共有10个数字,可以考虑枚举这10个数字的状态。
设f[s]表示所用的数字集合是s且集合中所有数均不超过n的方案总数。考虑s的一个子集v,用v组成一个数,s-v组成其余的数。
记n有p位:
1.如果|v|<p,那么v的任何一个排列都是满足条件的(当然0不能放在首位,需要特判);
2.如果|v|=p,那么我们一位一位考虑。
①对于某一位k,如果这一位放了一个小于n这一位的数(当然第一位不能放0),之后的数可以任意排列,方案数为(p-k)!;
②否则我们在这里放置和原数这一位一样的数(前提是这些数中有),继续处理k+1位;
③特别的,如果v完全等于n,答案需要加上1。算出v的方案数后,与f[s-v]相乘即为答案。
3.如果|v|>p,直接赋0。
最后把所有的f[1]~f[2^10-1]加起来就是答案了
#include<cstdio>const int MOD=1e9+7;const int N=15;const int M=(1<<10)+5;int n, ncnt, note[10];void Prep_digit() {scanf( "%d", &n );while(n) note[++ncnt]=n%10, n/=10;}int fac[N], inv[N];void Prep_FI() {//预处理出阶乘与阶乘的逆元fac[0]=fac[1]=inv[0]=inv[1]=1;for( int i=2; i<=10; i++) {fac[i]=1ll*fac[i-1]*i%MOD;inv[i]=1ll*inv[MOD%i]*( MOD-MOD/i )%MOD;}for( int i=2; i<=10; i++ ) inv[i]=1ll*inv[i]*inv[i-1]%MOD;}int f[M], bcnt[M], jud, scnt;void Prep_situ() {scnt=(1<<10);for( int s=0; s<scnt; s++ ) {//状态压缩, 用s的二进制数表示状态, 当前位是1就说明包含这个数bcnt[s]=bcnt[s>>1]+(s&1);//记录当前集合包含几个数if( bcnt[s]>ncnt ) f[s]=0;//比n的位数多else if( bcnt[s]<ncnt ) {//比n的位数少f[s]=fac[ bcnt[s] ];if( s&1 ) f[s]-=fac[ bcnt[s]-1 ];//减去第0位的情况数}else {//一样多f[ jud=s ]=0;bool ok=1;for( int i=ncnt; i; i-- ) {for( int j=(i==ncnt); j<note[i]; j++ )//首位不能为0if( jud&(1<<j) ) f[s]+=fac[ bcnt[jud]-1 ];if( !( jud&(1<<note[i]) ) ) { ok=0; break; }else jud-=(1<<note[i]);}f[s]+=ok;}}}int dp[N][N][M], ans;void Dp() {dp[0][0][0]=1;for( int i=1; i<=10; i++ )for( int j=1; j<=i; j++ )for( int s=1; s<scnt; s++ )if( bcnt[s]==i ) {if( j==1 ) dp[i][j][s]=f[s];else for( int k=1; k<i; k++ )for( int jud=(s-1)&s; jud; jud=(jud-1)&s ) if( bcnt[jud]==k )( dp[i][j][s]+=1ll*dp[k][j-1][jud]*f[s^jud]%MOD )%=MOD;}}void Print() {for( int i=1; i<=10; i++ )for( int j=1; j<=i; j++ )for( int s=0; s<scnt; s++ )( ans+=1ll*dp[i][j][s]*inv[j]%MOD )%=MOD;( ans+=MOD )%=MOD;printf( "%d\n", ans );}int main() {Prep_digit();Prep_FI();Prep_situ();Dp();Print();return 0;}
阅读全文
0 0
- [NOIP模拟赛]酷子集
- #NOIP模拟赛#Cool子集(Dp + 状压)
- 【NOIP模拟赛六】状态压缩DP day1 second 酷子集
- [NOIP模拟题][DP][高效算法][RMQ][子集]
- 【NOIP模拟考一】组合数学 day1 third 子集
- noip模拟赛 双城记
- 【noip模拟赛】密码
- 10.10NOIP模拟赛
- 10.08NOIP模拟赛
- 10.11NOIP模拟赛
- 10.12NOIP模拟赛
- 10.13NOIP模拟赛
- 【NOIP模拟赛】小奇挖矿
- NOIP模拟赛--军训
- 【NOIP模拟赛】数列
- noip模拟赛day8
- 16.1117 NOIP 模拟赛
- [NOIP模拟赛]单词
- JavaScript 正则表达式
- Flume中的拦截器(Interceptor)介绍与使用
- 将Vim改造为强大的IDE—Vim集成Ctags/Taglist/Cscope/Winmanager/NERDTree/OmniCppComplete
- javaee学习日记之javascript
- ffmpeg参数
- [NOIP模拟赛]酷子集
- Android中把Bitmap图片保存到文件中
- keras:2)函数式(Functional)模型
- 消除“Android NDK: WARNING: APP_PLATFORM”的方法
- POJ 2709 染料问题
- 进程
- ffmpeg工具调研
- 对lf_feature_storage性能测试分析(c测试用例版)
- [.cpp]统计双拼拼音一种方案的重码数