2022春训第四场

补题链接

总结另外写了,这里只是题解(补题记录)。

[B]

题意:

\([x,y]\) 内所有数字(作为一个0~9数字串)的顺序对之和。\(1 \leq x \leq y \leq 10^{14}\)

分析:

数位DP。可以预处理一个 \(f[i][t]\) 表示后面有 \(i\) 位数字可以任选,对当前位选 \(t\) 的贡献。那么
$ f[i][t] = \sum_{k=1}^{i} C_{i}^{k} (9-t)^k (t+1)^{i-k} k $
还有各种边角情况,都写在代码注释里了。此题比较核心的思路就是分开每一位算贡献,然后预处理和dfs结合。

代码如下
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int const N=20;
ll f[N][N],c[N],fac[N];
int a[N];
ll rd()
{
	char ch=getchar(); ll ret=0,f=1;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
	return ret*f;
}
ll pw(ll x,int y)
	{ll ret=1; while(y){if(y&1)ret*=x; x*=x; y>>=1;} return ret;}
ll C(int n,int m){return fac[n]/fac[m]/fac[n-m];}
void Init()
{
	fac[0]=1;
	for(int i=1;i<=14;i++)fac[i]=fac[i-1]*i;
	for(int i=1;i<=14;i++)
		for(int t=0;t<=9;t++)
		{
			for(int k=1;k<=i;k++)
				f[i][t] += C(i,k)*pw(9-t,k)*pw(t+1,i-k)*k; // i位可以自由选择,对当前位数字t的贡献
			c[i] += f[i][t]; // i位可以自由选择,对当前数字(0~9)的贡献
		}
	
}
ll get(ll x,int p){return x/pw(10,p);} // 第p位前面的数(不含p)
ll get2(ll x,int p){return x%pw(10,p-1);} // 第p位后面的数(不含p)
ll dfs(ll x,int p,int t) // 当前为第p位,前面已达到最大值,求对t的贡献
{
	if(p==0)return 0;
	ll ret=0;
	for(int k=0;k<a[p];k++) ret += f[p-1][t]; // 第p位取0~(a[p]-1)时,后面p-1位对t的贡献
	for(int k=t+1;k<a[p];k++) ret += pw(10,p-1); // 第p位取(t+1)~(a[p]-1)时,第p位对t的贡献
	if(a[p]>t) ret += get2(x,p)+1; // 第p位取a[p]时,第p位对t的贡献
	// printf("get2(%lld,%d)=%lld\n",x,p,get2(x,p));
	ret += dfs(x,p-1,t); // 第p位取a[p]时,后面p-1位对t的贡献
	// printf("dfs:p=%d t=%d ret=%lld\n",p,t,ret);
	return ret;
}
ll solve(ll x)
{
	int n=0; ll ret=0,tx=x;
	while(tx) a[++n]=tx%10, tx/=10;
	for(int i=1;i<=n;i++)
	{
		for(int t=1;t<a[i];t++) ret += f[i-1][t]; // 前面全0
		// printf("i=%d ret=%lld\n",i,ret);
		if(i<n)
		{
			for(int t=max(a[i],1);t<=9;t++) ret += f[i-1][t]; // 前面全0但前面可以不是0
			ret += c[i-1]*(get(x,i)-1); // 前面不是全0但未达最大,第i位任取0~9
			// printf("c[%d]=%lld get(%d)=%lld ret=%lld\n",i-1,c[i-1],i,get(x,i),ret);
			for(int t=0;t<a[i];t++) ret += f[i-1][t]; // 前面达到最大,第i位取0~(a[i]-1)时
		}
		ret += dfs(x,i-1,a[i]); // 前面已达最大值,第i位取a[i]
		// printf("i=%d ret=%lld\n",i,ret);
	}
	// printf("solve(%lld)=%lld\n",x,ret);
	return ret;
}
int main()
{
	Init();
	int T; scanf("%d",&T); int cs=0;
	while(T--)
	{
		cs++;
		ll x,y; scanf("%lld%lld",&x,&y);
		printf("Case %d: %lld\n",cs,solve(y)-solve(x-1));
	}
	return 0;
}
/*
5
1 9
1 100
50 60
23 2343
345 99373
*/

posted @ 2022-04-09 23:51  Zinn  阅读(29)  评论(0编辑  收藏  举报