想定解じゃない解法(ただし恐らく想定されている)で解いてしまった。
http://yukicoder.me/problems/448
問題
先ほどのEasyと同じ。
ただし満たすべきの対がN個ある。
解法
タイトルから想像できる通り、中国人剰余定理を解くだけで良い。
C++だと桁あふれを起こすので、以下Pythonで解いてます。
結構桁数が増えるので、PyPyじゃないとTLEします。
import sys import math def ext_gcd(p,q): if q==0: return (p,1,0) g,y,x = ext_gcd(q,p%q); y -= p/q*x return (g,x,y) def cmt(a1,mo1,a2,mo2): g,x,y=ext_gcd(mo1,mo2) a1%=mo1 a2%=mo2 if a1%g != a2%g: return (-1,0) lcm=mo1*(mo2/g) v=a1+((a2-a1)%lcm)*x%lcm*(mo1/g) return (((v%lcm)+lcm) % lcm,lcm) N=input() P=[] for i in range(N): P.append(map(int,raw_input().strip().split(" "))) T=[P[0][0],P[0][1]] for i in range(1,N): T2=cmt(T[0],T[1],P[i][0],P[i][1]) if T2[0]<0: print -1 sys.exit() T=[T2[0],T2[1]] if T[0]==0: T[0]=T[1] print T[0]%1000000007
別解でC++で多倍長を用いずに解く方法もある。
まず複数のY[i]が同じ素因数を持つ場合、一番指数が大きなY[i]だけその素因数を残し、他のY[x]からその素因数を取り除く。
例えば P mod 24 = 5, P mod 28 = 17だったら、2の指数は前者の24の方が大きいので、P mod 28 = 17はP mod 7 = 3とX[i],Y[i]を2^2で割り素因数から2を除く。
こうするとY[i]が互いに素になる。
あとはGarnerのアルゴリズムをそのまま適用すると良い。
(自分は残念ながらまだ理屈を理解していないが…)
X[i]がすべて0の時、解はLCM(Y[i])になるので注意。
ll ext_gcd(ll p,ll q,ll& x, ll& y) { // get px+qy=gcd(p,q) if(q==0) return x=1,y=0,p; ll g=ext_gcd(q,p%q,y,x); y-=p/q*x; return g; } ll inv(ll p,ll q) { // return (1/p)%q ( p,q is co-prime) ll xx,yy,g=ext_gcd(p,q,xx,yy); if(xx<0) xx+=q, yy-=p; return xx; } ll CRT_garner(vector<pair<ll,ll> > V, ll mo=1000000007) { const int prime_max = 40000; static int NP,prime[10000],divp[prime_max+3]; int i,j,k,l,r,x,y; string s; int N=V.size(); set<ll> P; vector<int> num; if(NP==0) { for(int i=2;i<prime_max;i++) if(divp[i]==0) { prime[NP++]=i; for(int j=i*i;j>i&&j<prime_max;j+=i) divp[j]=i; } } FOR(i,N) { ll v=V[i].second; FOR(j,NP) if(v%prime[j]==0) { P.insert(prime[j]); while(v%prime[j]==0) v/=prime[j]; } if(v>1) P.insert(v); } num=vector<int>(N,0); FORR(r,P) { y=0; FOR(x,N) { num[x]=1; j=V[x].second; while(j%r==0) j/=r, num[x]*=r; if(num[y]<num[x]) y=x; } FOR(x,N) if(x!=y) { if(V[x].first%num[x]!=V[y].first%num[x]) return -1; V[x].second /= num[x]; V[x].first %= V[x].second; } } vector<pair<ll,ll> > V2; FOR(x,N) if(V[x].second>1) V2.emplace_back(V[x].second,V[x].first); sort(V2.begin(),V2.end()); V=V2; N=V.size(); // garner FOR(y,N) FOR(x,y) { ll k=V[y].second-V[x].second; if(k<0) k+=V[y].first; V[y].second = k*inv(V[x].first,V[y].first)%V[y].first; } ll ret=0; for(x=N-1;x>=0;x--) ret = (ret*V[x].first+V[x].second)%mo; return ret; } void solve() { int N,i,x,y,nz=0; vector<pair<ll,ll> > V; cin>>N; FOR(i,N) { cin>>x>>y; V.emplace_back(x,y); nz+=x>0; } if(nz==0) { FOR(y,N) FOR(x,y) V[y].second/=__gcd(V[x].second,V[y].second); ll ret=1; FOR(x,N) ret=ret*V[x].second%1000000007; cout<<ret<<endl; } else { cout << CRT_garner(V) << endl; } }
まとめ
解く時間の8割は中国人剰余定理のライブラリの移植でした。
…多倍長整数で通してしまい、出題者に嘲笑われてるような気分です。