読者です 読者をやめる 読者になる 読者になる

メモ

yukicoderで遊んでいる競プロゆるふわ勢

yukicoder No.491 10^9+1と回文

問題はこちら
No.491 10^9+1と回文 - yukicoder

s<10^9に対して、s*(10^9+1)は、文字列としては「s,(いくつかの0),sを連結したもの」と一致する
これが回分数であることはsが回分数であることと同値
よって問題は「N/(10^9+1)以下の回分数の個数を求めよ」と読み替えられる。
対象となる数は高々9桁なので、5桁以下の数を元に回分数を構成し、それが求める範囲に入るかチェックすれば良い

#include <stdlib.h>
int main(){
	long n,len,ans=0;
	int i,j;
	char s[10];

	scanf("%ld",&n);
	n/=1000000001;
	for(i=1;i<100000;i++){
		sprintf(s,"%d",i);
		len=strlen(s);
		//1234->12344321
		for(j=len-1;j>=0;j--)s[len*2-1-j]=s[j];
		s[len*2]=0;
		if(strtoll(s,NULL,10)<=n)ans++;
		//1234->1234321
		for(j=len-2;j>=0;j--)s[len*2-2-j]=s[j];
		s[len*2-1]=0;
		if(strtoll(s,NULL,10)<=n)ans++;
	}
	printf("%d",ans);
	return 0;
}

ということで、これをざっくり書き換えてみる

long n,s;
//bを反転してaの後ろにくっつける関数。例えばf(1234,567)=1234765
f(a,b){return b?f(a*10+b%10,b/10):a;}
main(i){
	scanf("%ld",&n);
	//n/=1e9+1とすると、nがdoubleに変換されてしまい精度が足りない
	n/=1000000001;
	for(;i<1e5;i++){
		//iが5桁のとき、f(i,i)は10桁となりオーバーフローの可能性がある
		//nは9桁以下なのでこのケースは予め弾いてよい
		if(i<1e4&&f(i,i)<=n)s++;
		if(f(i,i/10)<=n)s++
	}
	printf("%d",s);
	//c11準拠になったのでreturn文を省略しても勝手に0を返してくれるようになったらしい
}

これをぎゅっとする

long n,x=1e9+1,s;
//sへの加算までfの中でやってしまう
f(a,b){!b?s+=a<=n:f(a*10+b%10,b/10);}
main(i){
	scanf("%ld",&n);
	for(n/=x;i<1e5;f(i++,i/10))i<1e4&&f(i,i);
	printf("%d",s);
}

135B


ちなみに上記プログラムは明らかにO(√N)だが、次のようにしてO(logN)で解くこともできる

//nの上半分を下半分に反転してコピーする。第2引数は次にコピーする桁
//f(1234567,10^6)=1234321、f(123456,10^5)=123321
f(n,t){return t>1?f(n%t/10,t/100)*10+n/t*(t+1):n;}

int main(){
	long n;
	int keta,temp,ans=0;

	scanf("%ld",&n);
	n/=1000000001;
	if(n==0){
		puts("0");
		return 0;
	}
	keta=log10(n)+1;
	ans=n/pow(10,keta/2)+pow(10,keta/2)-1;
	temp=pow(10,keta-1);
	if(f(n,temp)>n)ans--;
	printf("%d\n",ans);
}

次のような方法で計算している
1.N/1000000001をnとする。
2.nの桁数を求めketaとする(実際にはnが10べきのときに1ずれるが多分いい感じになっている)
3.keta桁未満の回分数の個数は
ketaが奇数のとき2*10^{\lfloor (keta-1)/2 \rfloor}-2
ketaが偶数のとき11*10^{\lfloor (keta-1)/2 \rfloor}-2で与えられる
(ちょうどk桁の回分数の総数が9*10^{\lfloor (k-1)/2 \rfloor}であることからわかる)
4.簡単のためまずnが回分数である場合について考える
ちょうどketa桁の回分数でn以下のものの個数は、nの上半分の桁を見ればわかり、\frac{n}{10^{\lfloor keta/2 \rfloor}}-10^{\lfloor (keta-1)/2 \rfloor}+1
(例えばn=1234321なら、1234-1000+1=235個、n=12344321なら1234-1000+1=235個)
5.nが回分数でないときに同様に上半分だけを見て確認すると、例えばn=654321のときにはn=654456と同じ計算をすることになるので、余分な回分数が入ってしまわないか確認するためにf(n,*)とnの大小関係を見る

この方針で縮めると156Bとなり全然及ばない

long n,x=1+1e9;
f(n,t){x=t>1?f(n%t/10,t/100)*10-n/t*~t:n;}
main(m){
	scanf("%ld",&n);
	printf("%d",n=~(f(n,x=pow(10,m=log10(n/=x)))>n)+(m=pow(10,-~m/2))+1.*n/m);
}