uva 10275 Guess the Number!
Guess the Number!
题意:给一个大数,问能否写成n^n。如果能,输出n,否则输出-1。
思路:除了1^1=1,2^2=4特殊外,其它数n^n所得到的数具有唯一的位数。这样可通过所给的大数的位数确定是哪个数,然后求n^n和所给的大数比较就行了。
n^n用快速幂来弄,然后用傅立叶大数相乘模板卡过去了,这个方法可能不是正解。

#include<stdio.h> #include<math.h> #include<stdlib.h> #include<string.h> #include<map> using namespace std; map<int,int>as; typedef struct vir{ double re,im; vir(){} vir(double a,double b){re=a;im=b;} vir operator +(const vir &b){ return vir(re+b.re,im+b.im);} vir operator -(const vir &b){ return vir(re-b.re,im-b.im);} vir operator *(const vir &b){ return vir(re*b.re-im*b.im,re*b.im+b.re*im);} }vir; vir x1[1000005],x2[1000005]; const double Pi = acos(-1.0); void change(vir *x,int len,int loglen) { int i,j,k,t; for(i=0;i<len;i++) { t = i; for(j=k=0;j<loglen;j++,t>>=1) k = (k<<1)|(t&1); if(k<i) { vir wt = x[k]; x[k] = x[i]; x[i] = wt; } } } void fft(vir *x,int len,int loglen) { int i,j,t,s,e; change(x,len,loglen); t = 1; for(i=0;i<loglen;i++,t<<=1) { s = 0; e = s + t; while(s<len) { vir a,b,wo(cos(Pi/t),sin(Pi/t)),wn(1,0); for(j=s;j<s+t;j++) { a = x[j]; b = x[j+t]*wn; x[j] = a + b; x[j+t] = a - b; wn =wn*wo; } s = e+t; e = s+t; } } } void dit_fft(vir *x,int len,int loglen) { int i,j,s,e,t=1<<loglen; for(i=0;i<loglen;i++) { t>>=1; s=0; e=s+t; while(s<len) { vir a,b,wn(1,0),wo(cos(Pi/t),-sin(Pi/t)); for(j=s;j<s+t;j++) { a = x[j]+x[j+t]; b = (x[j]-x[j+t])*wn; x[j] = a; x[j+t] = b; wn = wn*wo; } s = e+t; e = s+t; } } change(x,len,loglen); for(i=0;i<len;i++) x[i].re/=len; } void cal(char a[],char b[],char c[])//傅立叶大数相乘模板a*b=c { int i,len1,len2,t,over,len,loglen; len1 = strlen(a)<<1; len2 = strlen(b)<<1; len = 1; loglen = 0; while(len<len1) { len<<=1; loglen++; } while(len<len2) { len<<=1; loglen++; } for(i=0;a[i]!='\0';i++) { x1[i].re = a[i]-'0'; x1[i].im = 0; } for(;i<len;i++) x1[i].re = x1[i].im = 0; for(i=0;b[i]!='\0';i++) { x2[i].re = b[i]-'0'; x2[i].im = 0; } for(;i<len;i++) x2[i].re = x2[i].im = 0; fft(x1,len,loglen); fft(x2,len,loglen); for(i=0;i<len;i++) x1[i] = x1[i]*x2[i]; dit_fft(x1,len,loglen); for(i=(len1+len2)/2-2,over=loglen=0;i>=0;i--) { t = x1[i].re + over + 0.5; a[loglen++] = t%10; over = t/10; } while(over) { a[loglen++] = over%10; over /= 10; } for(loglen--;loglen>=0&&!a[loglen];loglen--); i = 0; if(loglen<0){ c[i++] = '0'; }else for(;loglen>=0;loglen--) c[i++] = a[loglen]+'0'; c[i] = 0; strcpy(a,c); } char a[500005],b[500005],c[500005],d[500005]; void fexp(int n,int m) { sprintf(a,"%d",1); sprintf(b,"%d",n); while(m){ if(m&1)cal(a,b,d); cal(b,b,d); m >>= 1; } } int main() { int i,k,j,t; for(i = 3; i <= 100000; ++ i){ k = log10(i) * i + 1; as[k] = i; } scanf("%d",&t); while(t--){ scanf("%s",c); k = strlen(c); if(k==1){ if(c[0]=='1')puts("1"); else if(c[0]=='4')puts("2"); else puts("-1"); } else if(as.find(k)!=as.end()){ k = as[k]; fexp(k,k); if(strcmp(a,c)==0)printf("%d\n",k); else puts("-1"); }else puts("-1"); } return 0; }
Guess the Number!的另外一种解法:
思路:由于n^n的特殊性,先用快速幂弄出n^n的后9位,然后比较。如果相等,则进行100次大数除法模拟,最后判断。
(实际上,数据很水,上面的都可以不要,只要测试一次大数除法就过了)

#include<stdio.h> #include<math.h> #include<stdlib.h> #include<string.h> #include<map> #define LL long long using namespace std; map<int,int>as; char c[500005]; int cnt; int fun(int x) { int i,j=0,k=0,t; cnt++; if(cnt>100)return 1;//控制100次 for(i = 0; c[i]; ++ i){ k = k * 10 + c[i] - '0'; t = k / x; while(k>=x)k-=x; if(j||t)c[j++]=t+'0'; } c[j]=0; return !k; } int fexp(LL a,int b,LL mod) { LL ans = 1; while(b){ if(b&1)ans = ans * a % mod; a = a * a % mod; b >>= 1; }return (int)ans; } int main() { int i,k,j,t; char a[100]; for(i = 3; i <= 100000; ++ i){ k = log10(i) * i + 1; as[k] = i; } scanf("%d",&t); while(t--){ scanf("%s",c); k = strlen(c); cnt = 0; if(k==1){ if(c[0]=='1')puts("1"); else if(c[0]=='4')puts("2"); else puts("-1"); } else if(as.find(k)!=as.end()){ k = as[k]; int d = fexp(k,k,1e9); sprintf(a,"%d",d); int la = strlen(a)-1; int lc = strlen(c)-1; while(la>=0){if(a[la]^c[lc])break;la--;lc--;}//比较尾数 if(la<0){ j = k; while(j--){if(!fun(k))break;} if(j<0)printf("%d\n",k); else puts("-1"); }else puts("-1"); }else puts("-1"); } return 0; }