(纪中)3056. 数字【数学】

119 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述


解题思路

首先我们要知道,求总合法的不能只简单的把两种情况的个数直接相加得到,因为他们有交集,要用容斥原理把它们的交集减去。( P s : 1221 Ps:1221 Ps1221 这个串,前后位和奇偶位都相等。)

预处理: d p [ i ] [ j ] dp[i][j] dp[i][j]代表在集合中选i个数,i个数的和为j的时候一共有多少种方案数.

状态转移方程: d p [ i , j ] = ∑ ( d p [ i − 1 , j − S [ k ] ] ) ( i = 1 dp[i,j]=∑(dp[i-1,j-S[k]]) (i=1 dp[i,j]=(dp[i1,jS[k]])(i=1~ n , j = 0 n, j=0 nj=0~ m a x n u m ∗ i , k = 1   ∣ S ∣ ) maxnum*i, k=1~|S|) maxnumik=1 S)
(maxnum为S中最大的数,因为最大能组成的和为 2 ∗ n ∗ m a x n u m 2*n*maxnum 2nmaxnum

则"前n位之和与后n位之和相等”和“奇数位之和与偶数位之和相等”的个数都是 ∑ i = 0 m a x n u m ∗ n d p [ n ] [ i ] ∑^{maxnum∗n}_{i=0}dp[n][i] i=0maxnumndp[n][i]
a n s + = ∑ i = 0 m a x n u m ∗ n d p [ n ] [ i ] ∗ 2 ans+=∑^{maxnum∗n}_{i=0}dp[n][i]∗2 ans+=i=0maxnumndp[n][i]2

接着考虑两种都符合的情况:假设前n个数中奇数位之和为a,偶数位之和为b,后n个数中奇数位之和为c,偶数位之和为d,易得:
a + b = c + d , a + c = b + d a+b=c+d,a+c=b+d a+b=c+da+c=b+d
所以: b = c , a = d b=c,a=d b=c,a=d
描述:前一块中的奇数和等于后一块的偶数和,前一块的偶数和等于后一块的奇数和

因为n可能是奇数,所以令k1为奇数块规模,k2为偶数块规模,我们发现这和之前的问题是一样的, k 1 = ( n + 1 ) / 2 , k 2 = n / 2 k1=(n+1)/2,k2=n/2 k1=(n+1)/2k2=n/2

则对“前一块中的奇数和等于后一块的偶数和”这一步的答案为 ∑ m a x n u m ∗ m a x i = 0 d p [ k 1 ] [ i ] ∗ d p [ k 1 ] [ i ] ∑_{maxnum∗max}^{i=0}dp[k1][i]∗dp[k1][i] maxnummaxi=0dp[k1][i]dp[k1][i] (手推一下可以知道无论n的奇偶前一块中的奇数个数等于后一块的偶数个数)

对“前一块的偶数和等于后一块的奇数和”的答案同样是上一行。

把两个答案乘起来即为之前两块的交集 a n s 1 ans1 ans1 a n s − = a n s 1 ans-=ans1 ans=ans1


代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#define ll long long
#define ldb long double
using namespace std;

const ll mod=999983;
ll ans,ans1,ans2,d[1010][9010];
int l,c,n,k1,k2;
char s[15];

int main(){
	scanf("%lld",&n);
	scanf("%s",s+1);
	l=strlen(s+1);
	for(int i=1;i<=l;i++)
		c=max(c,s[i]-'0');
	d[0][0]=1;	
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=i*c;j++)
		{
			for(int k=1;k<=l;k++)
			{
				if(j>=s[k]-'0')
					d[i][j]=(d[i][j]+d[i-1][j-(s[k]-'0')])%mod;
			}
		}
	}
	for(int i=0;i<=n*c;i++)
		ans=(ans+d[n][i]*d[n][i]%mod)%mod;
	ans=ans*2%mod;
	k1=(n+1)/2,k2=n/2;
	for(int i=0;i<=c*k1;i++)
		ans1=(ans1+d[k1][i]*d[k1][i]%mod)%mod;
	for(int i=0;i<=c*k2;i++)
		ans2=(ans2+d[k2][i]*d[k2][i]%mod)%mod;
	ans1=ans1*ans2%mod;
	printf("%lld",(ans+mod-ans1)%mod);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值