[USACO14OPEN] Odometer S

洛谷[USACO14OPEN] Odometer S

题目大意

当一个数的每一位中有至少一半的数字相同,那么这个数就是一个有趣的数。求区间 [ L , R ] [L,R] [L,R]中有多少个有趣的数。

100 ≤ L ≤ R ≤ 1 0 18 100\leq L\leq R\leq 10^{18} 100LR1018


题解

这道题很容易能想到要用数位DP。

[ L , R ] [L,R] [L,R]分为 [ 1 , L − 1 ] [1,L-1] [1,L1] [ 1 , R ] [1,R] [1,R],分别求出答案后作差即可得到最终答案。也就是说,我们只要想办法求出区间 [ 1 , v ] [1,v] [1,v]中的有趣的数的数量即可。设 v v v的位数为 d d d

先枚举这相同的一半位置的数字 i i i,设 f j , k , 0 / 1 / 2 f_{j,k,0/1/2} fj,k,0/1/2表示 v v v的从低到高的前 j j j中有 k k k位于 i i i相同,这个数与 v v v的前 j j j位相比是小于、等于还是大于。然后对于每种情况考虑转移即可。

为了避免统计含有前导零的数字,我们需要用 g g g来存储 f f f中各个状态的数的数量,以便于统计最后的答案。

但是,这样可能会重复计算多个数,我们要将重复计算的贡献减去。因为满足题意的数需要有至少一半的数字相同,所以这样的数中只会恰好有两种数字。当位数小于 d d d时,用组合数求出重复计算的贡献即可;当位数等于 d d d时,因为有一些数字大于 v v v,没有计算贡献,但如果用组合数计算贡献的话,会把这些当做重复的贡献来减去。所以我们要用 d f s dfs dfs把所有可能的数求出,在这些数中找出小于等于 d d d的并减去其重复计算的贡献。因为这样的数中只会恰好有两种数字,所以每次 d f s dfs dfs只会查找 2 d 2^d 2d个数。

数位DP的时间复杂度为 O ( d 3 ) O(d^3) O(d3),减去重复贡献的时间复杂度为 O ( 10 ⋅ 2 d ) O(10\cdot 2^d) O(102d),所以总时间复杂度为 O ( d 3 + 10 ⋅ 2 d ) O(d^3+10\cdot 2^d) O(d3+102d)

这个方法跑得比较快,目前在洛谷上是最优解。

[USACO14OPEN] Odometer S_第1张图片

code

#include
using namespace std;
int v1,v[25];
long long L,R,ct,ansL,ansR,wt,jc[20],f[25][25][3],g[25][25][3];
void dd(long long x){
	v1=0;
	while(x){
		v[++v1]=x%10;x/=10;
	}
}
int gt(int i,int j){
	if(i<j) return 0;
	if(i==j) return 1;
	return 2;
}
void dfs(long long now,int w,int hv,long long bg){
	if(w==1){
		if(hv==0&&now<=bg) ++ct;
		return;
	}
	dfs(now*10+wt,w-1,hv-1,bg);
	dfs(now*10+v[v1],w-1,hv+1,bg);
}
int main()
{
	scanf("%lld%lld",&L,&R);
	jc[0]=1;
	for(int i=1;i<=18;i++) jc[i]=jc[i-1]*i;
	dd(L-1);
	for(int i=0;i<=9;i++){
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		for(int j=0;j<=9;j++){
			++f[1][(i==j)][gt(j,v[1])];
			if(j>0) ++g[1][(i==j)][gt(j,v[1])];
		}
		for(int j=2;j<=v1;j++){
			for(int k=0;k<v1;k++){
				for(int p=0;p<=9;p++){
					if(p<v[j]){
						for(int o=0;o<=2;o++){
							f[j][k+(i==p)][0]+=f[j-1][k][o];
							if(p>0) g[j][k+(i==p)][0]+=f[j-1][k][o];
						}
					}
					else if(p==v[j]){
						for(int o=0;o<=2;o++){
							f[j][k+(i==p)][o]+=f[j-1][k][o];
							if(p>0) g[j][k+(i==p)][o]+=f[j-1][k][o];
						}
					}
					else{
						for(int o=0;o<=2;o++){
							f[j][k+(i==p)][2]+=f[j-1][k][o];
							if(p>0) g[j][k+(i==p)][2]+=f[j-1][k][o];
						}
					}
				}
			}
		}
		for(int j=1;j<=v1;j++){
			for(int k=(j+1)/2;k<=j;k++){
				ansL+=g[j][k][0]+g[j][k][1];
				if(j<v1) ansL+=g[j][k][2];
			}
		}
	}
	for(int o=2;o<v1;o+=2){
		for(int i=1;i<=9;i++){
			ansL-=jc[o-1]/jc[o/2]/jc[o/2-1];
			for(int j=1;j<i;j++){
				ansL-=jc[o]/jc[o/2]/jc[o/2];
			}
		}
	}
	if(v1%2==0){
		for(int o=1;o<v[v1];o++){
			for(int j=0;j<=9;j++){
				if(j==o) continue;
				ansL-=jc[v1-1]/jc[v1/2]/jc[v1/2-1];
			}
		}
		ct=0;
		for(wt=0;wt<=9;wt++){
			if(v[v1]==wt) continue;
			dfs(v[v1],v1,1,L-1);
		}
		ansL-=ct;
	}
	dd(R);
	for(int i=0;i<=9;i++){
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		for(int j=0;j<=9;j++){
			++f[1][(i==j)][gt(j,v[1])];
			if(j>0) ++g[1][(i==j)][gt(j,v[1])];
		}
		for(int j=2;j<=v1;j++){
			for(int k=0;k<v1;k++){
				for(int p=0;p<=9;p++){
					if(p<v[j]){
						for(int o=0;o<=2;o++){
							f[j][k+(i==p)][0]+=f[j-1][k][o];
							if(p>0) g[j][k+(i==p)][0]+=f[j-1][k][o];
						}
					}
					else if(p==v[j]){
						for(int o=0;o<=2;o++){
							f[j][k+(i==p)][o]+=f[j-1][k][o];
							if(p>0) g[j][k+(i==p)][o]+=f[j-1][k][o];
						}
					}
					else{
						for(int o=0;o<=2;o++){
							f[j][k+(i==p)][2]+=f[j-1][k][o];
							if(p>0) g[j][k+(i==p)][2]+=f[j-1][k][o];
						}
					}
				}
			}
		}
		for(int j=1;j<=v1;j++){
			for(int k=(j+1)/2;k<=j;k++){
				ansR+=g[j][k][0]+g[j][k][1];
				if(j<v1) ansR+=g[j][k][2];
			}
		}
	}
	for(int o=2;o<v1;o+=2){
		for(int i=1;i<=9;i++){
			ansR-=jc[o-1]/jc[o/2]/jc[o/2-1];
			for(int j=1;j<i;j++){
				ansR-=jc[o]/jc[o/2]/jc[o/2];
			}
		}
	}
	if(v1%2==0){
		for(int o=1;o<v[v1];o++){
			for(int j=0;j<=9;j++){
				if(j==o) continue;
				ansR-=jc[v1-1]/jc[v1/2]/jc[v1/2-1];
			}
		}
		ct=0;
		for(wt=0;wt<=9;wt++){
			if(v[v1]==wt) continue;
			dfs(v[v1],v1,1,R);
		}
		ansR-=ct;
	}
	printf("%lld",ansR-ansL);
	return 0;
}

你可能感兴趣的:(题解,题解)