20200215 SCOI模拟T1(数位dp)

Alice的幸运数

Alice的幸运数
题目背景
BJWC2013 T1

题目描述
在数学课上,Alice 知道了什么是最大公约数:
对于任意两个非零整数 x x x y y y,若整数 d d d 能同时被 x x x y y y 整除,则称 d d d x x x y y y 的公约数。定义 x x x y y y 的最大公约数 g c d ( x , y ) gcd(x,y) gcd(x,y) x x x y y y 的最大的公约数。
g c d ( 6 , 9 ) = 3 gcd(6,9)=3 gcd(6,9)=3 g c d ( 12 , 16 ) = 4 gcd(12,16)=4 gcd(12,16)=4 g c d ( 25 , 32 ) = 1 gcd(25,32)=1 gcd(25,32)=1,等等。
继而 Alice 对这样一类数产生了兴趣,并称它们为幸运数:
对于一个正整数 d d d ,我们使用 d i d_i di 表示 d d d 在十进制表示下,按从低位到高位顺序的第 i i i 位数字。
F ( d ) F(d) F(d) 表示 d d d 的奇数位的数字之和,即 F ( d ) = d 1 + d 3 + d 5 + … … F(d)=d_1+d_3+d_5+…… F(d)=d1+d3+d5+
G ( d ) G(d) G(d) 表示 d d d 的偶数位的数字之和,即 G ( d ) = d 2 + d 4 + d 6 + … … G(d)=d_2+d_4+d_6+…… G(d)=d2+d4+d6+
F ( d ) F(d) F(d) G ( d ) G(d) G(d) 均大于 0 ,且 F ( d ) F(d) F(d) G ( d ) G(d) G(d) 的最大公约数不超过 K K K ,则称 d d d 为幸运数。其中 K K K 是一个已知的常数。
举个例子来说,若 d = 641 d=641 d=641,则 d 1 = 1 d_1=1 d1=1 d 2 = 4 d_2=4 d2=4 d 3 = 6 d_3=6 d3=6 F ( 641 ) = 1 + 6 = 7 F(641)=1+6=7 F(641)=1+6=7 G ( 641 ) = 4 G(641)=4 G(641)=4。此时 F ( d ) F(d) F(d) G ( d ) G(d) G(d) 的最大公约数即 g c d ( 7 , 4 ) gcd(7,4) gcd(7,4) 等于 1 1 1 。则当 K K K 不小于 1 1 1 641 641 641 是幸运数。
你需要帮助 Alice 解答下面的问题:
对于给定的 K K K ,在不小于 L L L 并且不超过 R R R 的所有整数中,有多少个数是幸运数?
注意,输入文件包含多组测试数据。

输入格式
第一行包含一个整数 T T T ,表示有 T T T 组测试数据。
接下来 T T T 行,每行包含三个整数 K 、 L 、 R K、L、R KLR ,表示一次询问。

输出格式
输出 T 行,每行一个整数,依次表示每组测试数据的答案。

样例数据
输入 
5
1 1 10
2 28 34
100 987654321 987654321
1 1 50000
1 50001 100000

输出
0
5
1
30298
30309

备注
【样例解释】
K = 1 K=1 K=1 时, 1 1 1 10 10 10 之间不存在幸运数。
K = 2 K=2 K=2 时, 28 28 28 34 34 34 之间的幸运数有 28 、 29 、 31 、 32 、 34 28、29、31、32、34 2829313234,共 5 5 5 个。
K = 100 K=100 K=100 时, 987654321 987654321 987654321 是幸运数。
K = 1 K=1 K=1 时, 1 1 1 50000 50000 50000 之间的幸运数有 30298 30298 30298 个 , 50001 50001 50001 100000 100000 100000 之间的幸运数有 30309 30309 30309 个。

【数据规模和约定】
对于 10% 的数据: 1 ≤ L ≤ R ≤ 1 0 3 1≤L≤R≤10^3 1LR103
另有 10% 的数据: 1 ≤ L ≤ R ≤ 1 0 7 , 1 ≤ K , T ≤ 10 1≤L≤R≤10^7,1≤K,T≤10 1LR1071K,T10
另有 10% 的数据: 1 ≤ L ≤ R ≤ 1 0 9 , K = 1 , 1 ≤ T ≤ 10 1≤L≤R≤10^9,K=1,1≤T≤10 1LR109K=11T10
对于 60% 的数据: 1 ≤ L ≤ R ≤ 1 0 1 2 , 1 ≤ K , T ≤ 100 1≤L≤R≤10^12,1≤K,T≤100 1LR10121K,T100
对于 100% 的数据: 1 ≤ L ≤ R ≤ 1 0 1 8 , 1 ≤ K ≤ 100 , 1 ≤ T ≤ 1000 1≤L≤R≤10^18,1≤K≤100,1≤T≤1000 1LR10181K1001T1000

思路:
又双叒叕是万恶的数位dp,看来我要学一学了…

方法:预处理 D P [ i ] [ k ] [ s 0 ] [ s 1 ] DP[i][k][s_0][s_1] DP[i][k][s0][s1]表示还有i个数位没有填写,已填写的与i同奇偶的数位和等于 s 0 s_0 s0,不同奇偶的数位和等于 s 1 s_1 s1 K = k K = k K=k时的方案数。
转移方程: D P [ i ] [ k ] [ s 0 ] [ s 1 ] = ∑ D P [ i − 1 ] [ k ] [ s 1 − d ] [ s 0 ] , d = 0...9 DP[i][k][s_0][s_1]=∑DP[i-1][k][s_1-d][s_0],d=0...9 DP[i][k][s0][s1]=DP[i1][k][s1d][s0],d=0...9
将<=N的数按与N的公共前缀长度分类!
举个例子, N N N = 1234
分类:0xxx,10xx,11xx,120x,121x,122x,1230,1231,1232,1233,1234
这样,对于每个输入数据只需要枚举一遍所有类别的数

代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long

inline char ch() {
	static char buf[1<<21],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
}

inline int in {
	int s=0,f=1;
	char x;
	for(x=ch(); x<'0'||x>'9'; x=ch())	if(x=='=')	f=-1;
	for( ; x>='0'&&x<='9'; x=ch())	s=(s<<1)+(s<<3)+(x&15);
	return f==1?s:-s;
}

const int A=18;
const int B=82;
int dp[A][B][B][B];
int T;
int p,l,r;
int dit[A];

inline int gcd(int x,int y) {
	return !y?x:gcd(y,x%y);
}

inline void prepare() {
	for(int i=1; i<B; i++)
		for(int j=1; j<B; j++)
			for(int k=gcd(i,j); k<B; k++)
				dp[0][k][i][j]=1;
	for(int len=0; len<A-1; len++) {
		int s=(19-len)/2*9;
		for(int k=1; k<B; k++)
			for(int i=0; i<=s; i++)
				for(int j=0; j<=s; j++)
					for(int d=0; d<=9&&d<=j; d++)
						dp[len+1][k][j-d][i]+=dp[len][k][i][j];
	}
	return;
}

inline int work(int x) {
	if(x<=10)	return 0;
	if(x==1e18)	x--;
	int res=0,len=0;
	while(x) {
		dit[len++]=x%10;
		x/=10;
	}
	int a=0,b=0;
	for(int i=len-1; i>=0; i--) {
		for(int j=0; j<dit[i]; j++) {
			if(i&1)	res+=dp[i][p][b][a+j];
			else	res+=dp[i][p][a][b+j];
		}
		if(i&1)	a+=dit[i];
		else	b+=dit[i];
	}
	res+=dp[0][p][a][b];
	return res;
}

signed main() {
//	freopen("lucky.in","r",stdin);
//	freopen("lucky.out","w",stdout);
	prepare();
	T=in;
	while(T--) {
		p=in,l=in,r=in;
		if(p>81)	p=81;
		int res=work(r)-work(l-1);
		printf("%lld\n",res);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
中描述了一个幼儿园里分配糖果的问题,每个小朋友都有自己的要求。问题的输入包括两个整数NN和KK,表示幼儿园里的小朋友数量和要满足的要求数量。接下来的KK行表示小朋友们的要求,每行有三个数字,XX,AA,BB。如果X=1,表示第AA个小朋友分到的糖果必须和第BB个小朋友分到的糖果一样多;如果X=2,表示第AA个小朋友分到的糖果必须少于第BB个小朋友分到的糖果;如果X=3,表示第AA个小朋友分到的糖果必须不少于第BB个小朋友分到的糖果;如果X=4,表示第AA个小朋友分到的糖果必须多于第BB个小朋友分到的糖果;如果X=5,表示第AA个小朋友分到的糖果必须不多于第BB个小朋友分到的糖果。这个问题可以被看作是一个差分约束系统的问题。 具体地说,可以使用差分约束系统来解决这个问题。差分约束系统是一种通过给变量之间的关系添加约束来求解最优解的方法。对于这个问题,我们需要根据小朋友们的要求建立约束条件,并通过解决这个约束系统来得出最小的糖果数量。 在问题的输入中,X的取值范围为1到5,分别对应不同的关系约束。根据这些约束,我们可以构建一个差分约束图。图中的节点表示小朋友,边表示糖果数量的关系。根据不同的X值,我们可以添加相应的边和权重。然后,我们可以使用SPFA算法(Shortest Path Faster Algorithm)来求解这个差分约束系统,找到满足所有约束的最小糖果数量。 需要注意的是,在读取输入时需要判断X和Y是否合法,即是否满足X≠Y。如果X=Y,则直接输出-1,因为这种情况下无法满足约束条件。 综上所述,为了满足每个小朋友的要求,并且满足所有的约束条件,我们可以使用差分约束系统和SPFA算法来求解这个问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【差分约束系统】【SCOI2011】糖果 candy](https://blog.csdn.net/jiangzh7/article/details/8872699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [P3275 [SCOI2011]糖果(差分约束板子)](https://blog.csdn.net/qq_40619297/article/details/88678605)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值