A Claire_ 's problem 第八届北京邮电大学程序设计竞赛 - 热身赛 (2)

本文介绍了一种基于组合数学的游戏问题解决方法,涉及到排列组合、数位DP等算法技巧,通过具体的代码实现展示了如何计算使用特定数字组合可以形成的比原始数字小的正整数的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

时间限制 5000 ms 内存限制 65536 KB

题目描述

As we all know,Claire_ and ykwd are famous ACMers in BUPT ACM Group.They always play games together.Nowadays they play such a game:at first,Claire_ writes down a number,and then she selects a special digit(from 0 to 9),she removes all the special digits,then she get a group of non-special digits.Claire_ can use the group of non-special digits and limitless special digit to create new numbers.She is interested in how many positive numbers she create are less than the former number.She asks ykwd to solve this problem as soon as possible,otherwise ykwd would stand on his knees on the keyboard.Be notice leading zeros are not allowed.

输入格式

At first line,there is a numberT which is the number of cases;
For each case,there is a number N (length  1000) and a digit(from 0 to 9).

输出格式

For each case,you should print the answer in one line.Because the answer can be so large that you should print the answer mod 20130303.

输入样例

3
1020 0
1020 1
2110 1

输出样例

7
2
13

用例:

输入:777 7

输出:2



组合数学。

special digit为x.

设原数字长度是 n;

 则重组后的长度小于n的数肯定小于原数,这些数就是由 non-special digits 和 i(0<=i<=《special digit的个数》)个special digit组成;

可用排列组合算;假如求由  111333322222 的任意组合可组成数的个数,相当于3个A球,4个B球,5个C球放入12个盒子里,每个盒子放一个球。

答案为C(12,3)*C(9,4)*C(5,5); 求时注意一下没有前缀0;

还有就是所有的位数都用,长度为n,这时要保证组成的数小于原数,

设这个数为736890987,如果第一位取1--6(注意前缀0),后面的数是可以任意取的,用上面的方法算一下; 如果第一位为7,第二位只能取不超过3的数,第二位去0--2时,后面可任意取;第二位取3时,第三为只能取不超过6的数……,有点数位dp 不能超过上界的意思;然后一直加到末位。。



#include<stdio.h>    
#include<string.h>    
#include<math.h>    
#include<stdlib.h>    
#include<iostream>  
#include<algorithm>  
#include<queue>  
#include<vector>  
using namespace std;  
#define ll long long  
#define inf 0x3f3f3f3f  
#define N 1010  
  
char ch[1010];  
int p[1010];  
int bit[10];  
ll mod=20130303;  
  
ll c[N][N];  
int x;  
ll C(ll n,ll k)      
{      
    if(c[n][k]!=-1) return c[n][k];      
    if(k==1) return c[n][k]=n;      
    if(k==0) return c[n][k]=1;      
    if(n==k) return 1;      
    if(k > (n>>1)) return c[n][k]=C(n,n-k);      
    return c[n][k]=(C(n-1,k-1)+C(n-1,k))%mod;      
}   
ll calc(ll sum,ll n)  
{  
    int i;  
    ll ans=1;  
	int ff=0;
    for(i=0;i<10;i++)  
    {  
        if(i!=x && bit[i]==0) continue;  
        if(i==x && n==0) continue;  
		ff=1;
        if(i!=x){  
            if(i==0){  
                sum--;  
                ans=(ans*C(sum,bit[i]))%mod;  
                sum-=bit[i];  
                sum++;  
            }  
            else{  
                ans=(ans*C(sum,bit[i]))%mod;  
                sum-=bit[i];  
            }  
        }  
        else{  
            if(i==0){  
                sum--;  
                ans=(ans*C(sum,n))%mod;  
                sum-=n;  
                sum++;  
            }  
            else{  
                ans=(ans*C(sum,n))%mod;  
                sum-=n;  
            }  
        }  
    }  
	if(ff==0) return 0;
    return ans;  
}  
ll com(ll sum)  
{  
    ll ans=1;  
    int i;  
    for(i=0;i<10;i++)  
    {  
        if(!bit[i]) continue;  
        ans=(ans*C(sum,bit[i]))%mod;  
        sum-=bit[i];  
    }  
    return ans;  
}  
int main()  
{  
    int t,i,j,k;  
    cin>>t;  
    memset(c,-1,sizeof(c));  
    while(t--)  
    {  
        scanf("%s%d",ch,&x);  
        int len=strlen(ch);  
        memset(bit,0,sizeof(bit));  
        for(i=0;i<len;i++){  
            bit[ch[i]-'0']++;  
            p[i]=ch[i]-'0';  
        }  
        ll sum=0;  
        for(i=0;i<10;i++)  
            if(i!=x) sum+=bit[i];  
        ll ans=0;  
        for(i=0;i<bit[x];i++)  
        {  
            ans=(ans+calc(sum+i,i))%mod;  
        }  
        sum+=bit[x];  
        for(i=0;i<len;i++)  
        {  
            for(j=0;j<p[i];j++)  
            {  
                if(i==0 && j==0) continue;  
                if(!bit[j]) continue;  
                bit[j]--;  
                ans=(ans+com(sum-1-i))%mod;  
                bit[j]++;  
            }  
            bit[p[i]]--;  
        }  
        cout<<ans<<endl;  
    }  
}  




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值