The lucky numbers--Southeastern European Regional Programming Contest Bucharest, Romania 2009

The Lucky Numbers 
Time Limit: 1000ms, Special Time Limit:2500ms, Memory Limit:65536KB
Total submit users: 1, Accepted users: 1
Problem 10806 : No special judgement
Problem description
  John and Brus are freshmen at the primary school. Their first home task is to learn some integer numbers. It is not so hard for the guys and they decide to impress their teacher by learning all lucky numbers between A and B, inclusive.
As you already know from the previous year contest 4 and 7 are lucky digits, and all the other digits are not lucky. A lucky number is a number that contains only lucky digits in decimal notation.
After learning all lucky numbers in [A, B] range John and Brus still have some free time and now they decide to learn additionally each lucky number N that is out of [A, B] range, but the reversed number of N is in this range. Here reversed number of N is the number N written in decimal notation, but the order of digits is reversed. For example, the reversed number of 447 is 744 and reversed number of 774474444 is 444474477.
You are given integers A and B and your task is to find the total number of lucky numbers learned by John and Brus.

Input
  The first line contains single integer T ? the number of test cases. Each test case consists of a single line containing two integers A and B separated by a single space.

 

Constraints:

1 ≤ T ≤ 74,
1 ≤ A ≤ B ≤ 100000000000000000000000000000000000000000000000 (1047).


Output
  For each test case print a single line containing the total number of lucky numbers learned by John and Brus.

Sample Input
2 
1 100 
44 47
Sample Output
6 
3

 

http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=10806

http://www.acm.cs.ecnu.edu.cn/problem.php?problemid=2759 这个好像又不能进了

 

  题目大意:只包含4和7的数字称为幸运数字,给你A和B,求出在AB之间或者翻转后在AB之间的幸运数个数。

  思路:一个半小时;实现:2个小时;改BUG:5个小时。结论:55555,在比赛中我不可能AC的啊!!!

 

  这么大的数字完全不用考虑暴力啊。于是各种草稿,发现了一个不错的方法,总之我是要搞个函数f(num),得出翻转前后都小于等于num的数字的个数,由这个函数及其略微变形我可以求出翻转前后都比A小的,翻转前后都比B大的,翻转前比A小、翻转后比B大的,翻转后比A小、翻转前比B大的,总数减去这些就是答案了。

 

  其实我是先想到这个函数的大概构架再想到思路的。这函数怎么写呢?先找到翻转前必小于等于原数的数,然后找出这些数里翻转后必小于等于原数的数(=。=|||这是废话么?不是吧。。。)。比如4747,从左到右扫描到一个7,把它变成4,那么44XX必是比原数小的,把它翻转XX44,由于原数在XX的位置是47,不比47大的数有两个44、47,可得出44开头的数有两个是翻转前后都比4747小的。然后又扫描到一个7,变成4744,翻转4474,它比4747小,也是一个。刚才找的都是小于4747的,4747本身不符合条件,不用加1。这样就得出翻转前后都比4747小的数有3个。当然这只是大概思路,还有好多细节,比如7744,扫描到第二个7变成74XX,翻转后是XX47,按刚才说法该有4(77->44 47 74 77)种,但由于47>44,所以其中相等的一种情况(77)要减去,7747>7744,而其他依然是满足条件的。还有个注意的就是找完所有必小于原数的数后还要考虑本身是不是翻转后也小于等于自身,这个也考虑进去这个函数就差不多完整了。

 

  由于我这个函数是求小于等于的,先要把A减1,然后要把AB都转变成不大于自己的最大的幸运数A'B'。这个范围里的幸运数跟原来AB间的幸运数个数一样的。然后用那函数求出翻转前后小于等于A'的个数。那大于B'的呢?长度比B'小的肯定翻转前后都小于它了,长度比它大的不需要考虑,那长度一样的里面呢,根据容斥定理,答案=总数-翻转前小于等于B'的个数-翻转后小于等于B'的个数+翻转前后都小于等于B'的个数。最后呢要求翻转前比A小、翻转后比B大的,显然,这个的个数和翻转后比A小、翻转前比B大的个数是相等的。如果写出了上面那个函数,就会发现略微变下形就可以求出这个了。然后就可以A了啦~~~~~~

 

  感觉这题很考验考虑问题的周到程度,各种细节各种bug,不知道过个半年做这种题目能不能快点。

 

 #include<iostream> #include<cstring> #include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; __int64 f_pow(__int64 x,int n){ __int64 res=1; while(n--)res*=x; return res; } void f_reverse(char *x,char *y){ int i,len=strlen(y); for(i=0;i<len;i++)x[i]=y[len-1-i]; x[len]=0; } bool all47(char *x){//判断是不是都是47 int i,len=strlen(x); for(i=0;i<len;i++){ if(x[i]!='4'&&x[i]!='7')return 0; } return 1; } char sa[100],sb[100]; bool less_than_all4(char *x){//判断是不是比相同长度的全4的数字还要小 int i; for(i=0;x[i];i++){ if(x[i]<'4')return 1; if(x[i]>'4')return 0; } return 0; } void get_nobig(char *x){//找到不比x大的最大幸运数 int i,ti,len=strlen(x); if(less_than_all4(x)){//比全4都小就变成短一位的全7 for(i=0;i<len-1;i++)x[i]='7'; x[i]=0; return; } for(i=0;x[i];i++){ if(x[i]>'7'){ x[i]='7'; i++; break; } if(x[i]=='7'){ x[i]='7'; continue; } if(x[i]>'4'){ x[i]='4'; i++; break; } if(x[i]=='4'){ x[i]='4'; continue; } x[i]='4'; ti=i-1; while(true){ if(x[ti]=='7'){ x[ti]='4'; break; } x[ti]='7'; ti--; } break; } for(;x[i];i++)x[i]='7'; x[i]=0; } __int64 get_num(char *x){//返回x的长度下小于等于x的幸运数个数 int len=strlen(x),i; __int64 res=0,two=1; for(i=len-1;i>=0;i--){ if(x[i]=='7')res+=two; two*=2; } return res+1; } __int64 get_num2(char *x,int len){//返回len的长度下大于等于x的len长度前缀的幸运数个数 int i; __int64 res=0,two=1; for(i=len-1;i>=0;i--){ if(x[i]=='4')res+=two; two*=2; } //4774 return res+1; } __int64 get_num(char *x,int len){//返回len的长度下小于等于x的len长度前缀的幸运数个数 int i; __int64 res=0,two=1; for(i=len-1;i>=0;i--){ if(x[i]=='7')res+=two; two*=2; } return res+1; } __int64 nobig_num(char *x){//咦,这个好像可以去掉了,把if加到那个函数里就好了 if(x[0]==0)return 0; return get_num(x); } bool spcmp(char *x,int loc){//x的loc长度前缀翻转后大于x的loc长度后缀则返回true int len=strlen(x),i; if(x[len-1-loc]=='7')return 0; for(i=loc-1;i>=0;i--){ if(x[i]>x[len-1-i])return 1; if(x[i]<x[len-1-i])return 0; } return 0; } bool spcmp(char *x,char *y,int loc){//x的loc长度前缀翻转后大于y的loc长度后缀则返回false int len=strlen(x),i; if(y[len-1-loc]=='7')return 1; for(i=loc-1;i>=0;i--){ if(x[i]>y[len-1-i])return 0; if(x[i]<y[len-1-i])return 1; } return 1; } __int64 all_in(char *nb){//得到翻转前后都小于等于它的 if(nb[0]==0)return 0; char rev[100]; int len=strlen(nb),i; __int64 res=f_pow(2,len)-2; for(i=0;i<len;i++){ if(nb[i]=='4')continue; res+=get_num(nb,len-1-i); if(spcmp(nb,i))res--; } f_reverse(rev,nb); if(strcmp(nb,rev)>=0)res++; return res; } __int64 all_out(char *nb){//得到翻转前后都大于它的个数 if(nb[0]==0)return 0; int len=strlen(nb); __int64 in=all_in(nb),slk=nobig_num(nb); return 2-slk*2+in; //pow(2,len)-(slk*2-in+(pow(2,len)-2)) } __int64 in_out(char *x,char *y){//得到翻转前小于等于x,翻转后大于y的个数 int i; char rev[100]; __int64 res=0; int len=strlen(x); if(len<strlen(y))return 0; if(x[0]==0)return 0; for(i=0;i<len;i++){ if(x[i]=='4')continue; res+=get_num2(y,len-1-i); if(spcmp(x,y,i))res--; } f_reverse(rev,x); if(strcmp(rev,y)>0)res++; return res; } void run(){ __int64 res1,res2,res3,tres; int la=strlen(sa),i,lb; sa[la-1]--; i=la-1; while(sa[i]<'0'){ sa[i]='9'; sa[--i]--; }//把A减1 get_nobig(sa); get_nobig(sb); lb=strlen(sb); res1=all_in(sa);//翻转前后都小于等于sa的数 res2=all_out(sb);//翻转前后都大于sb的数 res3=in_out(sa,sb);//翻转前小于等于sa,翻转后大于sb的数 tres=f_pow(2,lb+1)-2-2*res3-res2-res1; printf("%I64d/n",tres); } // 4774 4777 // res1 4444 4474 4744 4774 // res2 7447 7477 7747 7777 // res3 4447 4477 4747 7444 7744 7474 int main(){ int t; scanf("%d",&t); while(t--){ scanf("%s%s",sa,sb); run(); } return 0; }

你可能感兴趣的:(The lucky numbers--Southeastern European Regional Programming Contest Bucharest, Romania 2009)