さてここからが本番。
GCJとしては珍しくLargeが2段階ある。
https://code.google.com/codejam/contest/2270488/dashboard#s=p2
問題
ある数がFair and Squareであるというのは、その数字が回文である上に、回文の二乗になっていることである。
2つの数A,Bが与えられたとき、A<=X<=Bに当てはまるFair and Squareな数Xの数を答えよ。
解法(Small~Large1)
このA<=X<=Bに当てはまる数を答える問題、GCJに多いね。
これば0~Bに当てはまる数から、0~(A-1)に当てはまる数を引けばよい。
0~Bに当てはまるはずだけど、Bは高々10^14。
よって10^7までの数の二乗になっているはず。
高々10^7ならループを回しても間に合うので、先に10^7までの数のうち回文になるものを二乗し、その値も回文になるものをFair and Squareな数を列挙してしまえばよい。
ちなみに最大の10^14までのうちFair and Squareなものは39個しかない。
Fair and Squareな数を列挙できたら、後はB以下の数を数えればよい。
vector<ll> list; int parp(char* str) { int i,l; l=strlen(str); FOR(i,l) if(str[i]!=str[l-1-i]) return 0; return 1; } int parp(ll n) { char h[200]; sprintf(h,"%lld",n); return parp(h); } int num(ll L) { int i; while(i<list.size()-1 && L>=list[i]) i++; return i; } ll A,B; void solve(int _loop) { scanf("%lld %lld",&A,&B); _PE("Case #%d: %d\n",_loop,num(B)-num(A-1)); } void init() { ll i; FOR(i,10000003) { if((i/100)%10>=3) {i+=99; continue;} if((i/10)%10>=3) {i+=9; continue;} if(i%10>=4) continue; if(parp(i) && parp(i*i)) list.push_back(i*i); } }
解法(Large2)
こちらは桁数が10^100まで増えるので、10^50までの回文を二乗する…のは計算量的に無理。
試しに先の10^14までのケースを見ると、以下の法則がわかる。
- 回文を二乗する場合、各桁で繰り上がりが発生してはならない
- Fair and Squareな数では、最大値は真ん中の桁にくる
よって、真ん中の桁が9以下になるようにすればよい。
2乗しても真ん中の桁が9以下になる数値は以下のパターンである。
これらに対して2乗した値がFair and Squareな数になる。
- 1桁の場合
- 1か4か9のみ
- 3ケタ以上の奇数桁の場合
- 真ん中が2の場合
- 両端の桁は1のみ。先頭と真ん中の間に、1があと0~1個入り、あとは0。
- 真ん中の桁は0か1の場合
- 両端の桁が2の場合、真ん中と先頭の桁の間は0のみ。
- 両端の桁が1の場合、先頭と真ん中の間に、1があと0~3個入り、あとは0。
- 真ん中が2の場合
- 偶数桁の場合
- 両端の桁が2の場合、真ん中と先頭の桁の間は0のみ。
- 両端の桁が1の場合、先頭と真ん中の間に、1があと0~3個入り、あとは0。
これらの数は10^50までに41548個しかない。
あとはSmall~Large1同様数を数えるだけ。
一応先の2法則は証明できそうだ。
1つ目はあるところで桁上がりが発生すると、そこと対象の位置で逆方向の桁上がり?が発生しないと回文にならないので、途中で桁上がりは発生できない。
2つ目は、例えば2つの桁の値xとyがあったとき、真ん中の桁はx^2+y^2が加算されるが、別の桁は2xyが加算される。相加相乗平均の法則より、前者の方が大きい。
vector<ll> list; int C[102][102]; vector<string> str[201]; int parp(char* str) { int i,l; l=strlen(str); if(l==1) return (str[0]=='1' ||str[0]=='4' || str[0]=='9'); FOR(i,l) if(str[i]!=str[l-1-i]) return 0; return 1; } void enums(int keta) { char s[300]; int i,x,y,z,l; ZERO(s); FOR(i,keta) s[i]='0'; if(keta%2==1) { // top=2 s[0]=s[keta-1]='2'; str[keta*2-1].push_back(s); s[(keta-1)/2]='1'; str[keta*2-1].push_back(s); // top=1 mid=2 s[0]=s[keta-1]='1'; s[(keta-1)/2]='2'; str[keta*2-1].push_back(s); for(i=1;i<keta/2;i++) { s[i]=s[keta-1-i]='1'; str[keta*2-1].push_back(s); s[i]=s[keta-1-i]='0'; } FOR(l,2) { s[(keta-1)/2]='0'+l; str[keta*2-1].push_back(s); for(x=1;x<keta/2;x++) { s[x]=s[keta-1-x]='1'; str[keta*2-1].push_back(s); for(y=x+1;y<keta/2;y++) { s[y]=s[keta-1-y]='1'; str[keta*2-1].push_back(s); for(z=y+1;z<keta/2;z++) { s[z]=s[keta-1-z]='1'; str[keta*2-1].push_back(s); s[z]=s[keta-1-z]='0'; } s[y]=s[keta-1-y]='0'; } s[x]=s[keta-1-x]='0'; } } } else { // top=2 mid=0 s[0]=s[keta-1]='2'; str[keta*2-1].push_back(s); // top=1 mid=2 s[0]=s[keta-1]='1'; str[keta*2-1].push_back(s); for(x=1;x<keta/2;x++) { s[x]=s[keta-1-x]='1'; str[keta*2-1].push_back(s); for(y=x+1;y<keta/2;y++) { s[y]=s[keta-1-y]='1'; str[keta*2-1].push_back(s); for(z=y+1;z<keta/2;z++) { s[z]=s[keta-1-z]='1'; str[keta*2-1].push_back(s); s[z]=s[keta-1-z]='0'; } s[y]=s[keta-1-y]='0'; } s[x]=s[keta-1-x]='0'; } } // square s[keta*2-1]=0; FOR(i,str[keta*2-1].size()) { FOR(x,keta*2-1) s[x]='0'; FOR(x,keta) { if(str[keta*2-1][i][x]!='0') FOR(y,keta) s[(keta*2-2)-(x+y)]+=(str[keta*2-1][i][keta-1-x]-'0')*(str[keta*2-1][i][keta-1-y]-'0'); } str[keta*2-1][i]=s; } sort(str[keta*2-1].begin(),str[keta*2-1].end()); } int enumk(int keta) { int num; if(keta==1) return 3; if(keta%2==1) { num = 2; // top=2,mid=0,1 // top=1,mid=0,1,left=0,1,2,3 num += 2*(C[(keta-3)/2][0]+C[(keta-3)/2][1]+C[(keta-3)/2][2]+C[(keta-3)/2][3]); // top=1,mid=2,left=0,1 num += (C[(keta-3)/2][0]+C[(keta-3)/2][1]); } else { num = 1; // top=2, mid=0 // top=1,left=0,1,2,3 num += C[(keta-2)/2][0]+C[(keta-2)/2][1]+C[(keta-2)/2][2]+C[(keta-2)/2][3]; } return num; } int num(char *st, int inc) { int l=strlen(st); int n=0,i; FOR(i,l) n+=str[i].size(); FOR(i,str[l].size()) { if((inc && str[l][i]>st) || (!inc && str[l][i]>=st)) break; n++; } return n; } ll A,B; void solve(int _loop) { char A[200]; char B[200]; GETs(A); GETs(B); _PE("Case #%d: %d\n",_loop,num(B,1)-num(A,0)); } void init() { ll i; int x,y; ZERO(C); FOR(x,101) C[x][0]=C[x][x]=1; for(x=1;x<=100;x++) for(y=1;y<x;y++) C[x][y]=C[x-1][y-1]+C[x-1][y]; str[1].push_back("1"); str[1].push_back("4"); str[1].push_back("9"); for(x=2;x<=50;x++) enums(x); }
まとめ
Large2まできちっと解き切れてよかった。
面白い問題だったね。