2019.09.18【NOIP提高组】模拟 A 组

T1:显然直接斜着做前缀和就好了。

比赛时我有一个地方没有开long long,所以90分。大数据不能代替检查和对拍。

T2:这题有很强的思维。

设f[i][j][0/1]表示前i位,是否顶到上限(0/1)的数字j的个数。注意这里的个数是算上所有情况的。

之所以可以这样设dp是因为我们最终只用关心所有情况合起来时每个数字出现的总次数,至于每个数字具体在哪一个数中出现并不重要。

然后考虑转移f。

首先我们要把之前的f都加上,即f[i+1][j][0]+=f[i][j][0]*10(前i位有f[i][j][0]个数字,第i+1位可以填0~9共10个数字,所以有10种情况)。f[i][j][1]同理讨论。

然后我们要算上i+1位的新的贡献。对于f[i+1][j][1],其实i+1位的贡献就等于1(在j=a[i+1]时),这个很好理解。对于f[i+1][j][0],i+1位的贡献就是a[1~i]-1,即前面的数的方案数。

其实这些方程式都不是绝对的,都要因具体的j和a[i+1]而定。

那么现在就假设我们已经算出了f,然后要求答案。对于一个第i位填j,前面的数对它的贡献就是sum(f[i-1][x][0/1])(x>j),然后这个贡献还要乘上它后面的可填的数的个数(即10^(n-i)或a[i+1~n]+1)。

至此,这题我们就做出来了。

细节较多,贴一下代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#define ll long long
#define MAXN 500010
#define mod 998244353

ll f[MAXN][11][2],a[MAXN],s[MAXN],b[MAXN],T,num,n,ans;
char a1[MAXN];
ll sqr(ll x,ll y)
{
	ll j=x,s=1,w=y;
	while(w>=1)
	{
		if(w%2==1)s=(s*j)%mod;
		j=(j*j)%mod;w/=2;
	}
	return s;
}
ll dp()
{
	ll i,j,k,x,w0,w1;
	memset(f,0,sizeof(f));
	for(i=0;i<a[1];i++)f[1][i][0]=1;
	f[1][a[1]][1]=1;
	s[1]=a[1];
	for(i=1;i<n;i++)
	{
		for(j=0;j<=9;j++)
		{
			if(j>=a[i+1])f[i+1][j][0]=(f[i+1][j][0]+f[i][j][0]*10+f[i][j][1]*a[i+1]+s[i])%mod;
			else f[i+1][j][0]=(f[i+1][j][0]+f[i][j][0]*10+f[i][j][1]*a[i+1]+s[i]+1)%mod;
			if(j==a[i+1])f[i+1][j][1]=(f[i+1][j][1]+f[i][j][1]+1)%mod;
			else f[i+1][j][1]=(f[i+1][j][1]+f[i][j][1])%mod;
		}
		s[i+1]=(s[i]*10+a[i+1])%mod;
	}
	s[n+1]=0;
	s[n]=a[n];
	for(i=n-1;i>=1;i--){s[i]=(s[i+1]+a[i]*b[n-i])%mod;}
	ans=0;
	for(i=2;i<=n;i++)
	{
		w0=0;w1=0;
		for(j=9;j>=0;j--)
		{
			if(j<a[i])ans=(ans+(w0+w1)*b[n-i])%mod;
			else
			{
				ans=(ans+w0*b[n-i])%mod;
				if(j==a[i])ans=(ans+w1*(s[i+1]+1))%mod;
			}
			w0=(w0+f[i-1][j][0])%mod;
			w1=(w1+f[i-1][j][1])%mod;
		}
	}
	return ans;
}
int main()
{
//freopen("pair.in","r",stdin);
//freopen("pair.out","w",stdout);
ll i,j,w;
scanf("%lld %lld\n",&T,&num);
b[0]=1;
for(i=1;i<=500000;i++)b[i]=b[i-1]*10%mod;
while(T>=1)
{
	scanf("%s ",a1);
	n=strlen(a1);
	memset(a,0,sizeof(a));
	for(i=1;i<=n;i++)a[i]=a1[i-1]-'0';
	a[n]--;
	i=n;
	while(a[i]<0){a[i]+=10;a[i-1]--;i--;}
	w=-dp();
	scanf("%s\n",a1);
	n=strlen(a1);
	for(i=1;i<=n;i++)a[i]=a1[i-1]-'0';
	w=w+dp();
	printf("%lld\n",(w+mod)%mod);
	T--;
}
}

T3:题解待更新。

发布了149 篇原创文章 · 获赞 24 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/chiyankuan/article/details/101122793