数字 (求2类数∩的方法)

数字

(num.c/cpp/pas)

【问题描述】

    一个数字被称为好数字当他满足下列条件:

    1. 它有2*n个数位,n是正整数(允许有前导0)

    2. 构成它的每个数字都在给定的数字集合S中。

    3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等

    例如对于n=2,S={1,2},合法的好数字有1111,1122,1212,1221,2112,2121,2211,2222这样8种。

    已知n,求合法的好数字的个数mod 999983。

【输入格式】

    第一行一个数n。

    接下来一个长度不超过10的字符串,表示给定的数字集合。

【输出格式】

    一行一个数字表示合法的好数字的个数mod 999983。

【样例输入】

    2

    0987654321

【样例输出】

    1240

【数据规模】

    对于20%的数据,n≤7。

    对于100%的.据,n≤1000,|S|≤10。



这题难点在求前n位之和与后n位之和相等与它奇数位之和与偶数位之和相等的交集

……

记得第一个数一定要(long long ) c++中对 3000这样的常量都是算int的,而等号优先级最低


另外本题是乘法原理+加法原理


先用乘法原理 求出 A=x a=y B=y b=x 的方案数 记得%F

然后将所有的情况相加++


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
#include<iostream>
using namespace std;
#define F (999983)
#define MAXN (1000+10)
#define MAXM (10000+10)
int n,m;
char s[100];
int a[100],siz=0;
int f[MAXN][MAXM];
int main()
{
    freopen("num.in","r",stdin);
  freopen("num.out","w",stdout);
    scanf("%d",&n);
    scanf("%s",s);
    memset(f,0,sizeof(f));
    for (int i=0;i<strlen(s);i++)
        a[++siz]=s[i]-'0';
    
//    for (int i=1;i<=siz;i++) cout<<a[i]<<' ';
    sort(a+1,a+1+siz);
      for (int i=1;i<=siz;i++) 
      {
          f[1][a[i]]=1;
//          cout<<a[i]<<' ';
      }
    f[0][0]=1;
    
    for (int i=2;i<=n;i++)
        for (int j=i*a[1];j<=i*a[siz];j++)
        {
   /*         if (j==18)
            {
                      cout<<'s';
                      }
   */         for (int k=1;k<=siz;k++) 
                if (j-a[k]>=0)
                     f[i][j]=(f[i][j]+(f[i-1][j-a[k]])%F)%F;
        }
    long long ans=0;
    for (int j=0;j<=n*a[siz];j++) 
    {
 //   if (f[n][j]>0) cout<<j<<' '<<f[n][j]<<endl;     
        ans=ans+(long long)(f[n][j])*(long long)(f[n][j]);
    	ans%=F;
	}
    
    
  //  cout<<(2*ans)%F<<endl;
  	
  	/*现在开始几算相同的情况
  	A 前奇 B 前偶 a 后奇 b 后偶 
  	
	  显然 A+a=B+b A+a=B+b  ->A=b&&sa=B
	  
	  则答案为
	  
	  ∑(a*A*b*B)    这4个数为(此处a,A,b,B指当它们正好等于这个值的方案数
	  ∑ (a^2*A^2)
	  ∑ (f[n/2.i]^2*f[n/2+n%2,i])    
	  
	*/
//	cout<<ans<<endl;
	long long tmpA=0;
	for (int i=(n/2)*a[1];i<=(n/2)*a[siz];i++)
	  tmpA=(tmpA+(long long)f[n/2][i]*f[n/2][i])%F;
	
	long long tmpB=0;
	for (int i=(n/2+n%2)*a[1];i<=(n/2+n%2)*a[siz];i++)
	  tmpB=(tmpB+(long long)f[n/2+n%2][i]*f[n/2+n%2][i])%F;
	
//	cout<<tmpA<<' '<<tmpB;
	  
//	 cout<<tmpA*tmpB<<endl;
	  
	ans=(ans*2)%F;
//	cout<<ans<<endl;
    
    {
//		cout<<ans<<' '<<tmpA*tmpB;
	}
	
	
	
	
	ans=(ans-tmpA*tmpB+F*((tmpA*tmpB)/F)+F)%F; 
    
    
    cout<<ans<<endl;
    
    
    
 //  while(1);
   return 0;
}




 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值