Solution:SP27519 Z124H - Zeros of the fundamental Fibonacci period
Argon_Cube · · 题解
请先阅读 Easy Version SP27484 Z124 - Zeros in Fibonacci period 的题解。
垃圾高精卡常题/tuu/tuu 下面看起来写的很少但是每一步卡常都要试好久,卡了两天终于过了。
和 SP27484 的做法是完全一样的,但是我们发现
然后我们发现这个时候因为高精时间复杂度是
首先我们要先整一个压位高精,这样常数就可以小到消掉一个
然后我们直接把板子粘过来发现本地跑了
这个时候你发现直接高精是过不去 SP27484 的(这个题前两组数据和时限和 SP27484 一样),必须要特判
这个时候发现 SPOJ 是不开 O2 的,于是我们直接加一个 #pragma 手动开一下 O2,虽然还是过不去但是通过一些测试发现此时我们在 SPOJ 上能跑出来大约
pairiinf fib(intinf n,const intinf& p)
{
if(n==0)
return (pairiinf){0,1};
if(n%2==1)
{
auto c=fib(n-1,p);
return (pairiinf){(c.a+c.b)%p,c.a};
}
auto c=fib(n/2,p);
return (pairiinf){c.a*(c.b*2+c.a)%p,(c.a*c.a+c.b*c.b)%p};
}
这个是我们一开始算 intinf 是一个封装好的 GMP 高精类。我们通过反复的删代码发现,本地的 n%2 n-1 n/2 上面??这个时候我们阅读一下 gmp.h 的源码,发现 GMP 内部存储是按二进制位压
pairiinf fib(const intinf& n,const intinf& p)
{
intinf a=0,b=1;
const auto& m=n.n[0];
ford(i,m._mp_size-1,0)
ford(j,63,0)
{
auto c=a*(a+b+b),d=a*a+b*b;
a=c%p,b=d%p;
if(m._mp_d[i]&1ull<<j)
(b+=a)%=p,swap(a,b);
}
return {a%p,b%p};
}
然后还是过不去??这个时候就开始玄学了,经过尝试发现乘法次数减少了好像耗时更长了,那我们减少一点取模,当 b+=a 时不取模,最后再把
但是还是过不去,我们看看能不能减少一点赋值操作,然后经过许多次尝试发现下面的写法是本地唯一能卡进
pairiinf fib(const intinf& n,const intinf& p)
{
intinf a=0,b=1;
const auto& m=n.n[0];
ford(i,m._mp_size-1,0)
ford(j,63,0)
{
auto d=a*a+b*b;
(a*=(a+b+b))%=p,b=d%p;
if(m._mp_d[i]&1ull<<j)
b+=a,swap(a,b);
}
return {a%p,b};
}
竟然还是过不去??那我把最后的 a==0||a==p 总行了吧?还是过不去???
无语了什么垃圾卡常题,这都不给我过吗?本地都能跑到
#pragma GCC optimize(2)
#include <algorithm>
#include <iostream>
#include <cassert>
#include <cstring>
#include <vector>
#include <bitset>
#include <string>
#include <array>
#include <gmp.h>
typedef int i32;
typedef unsigned int u32;
typedef long long i64;
typedef unsigned long long u64;
typedef __int128_t i128;
typedef __uint128_t u128;
struct fastIO
{
static const int BUFF_SZ = 1 << 18;
char inbuf[BUFF_SZ], outbuf[BUFF_SZ];
fastIO()
{
setvbuf(stdin, inbuf, _IOFBF, BUFF_SZ);
setvbuf(stdout, outbuf, _IOFBF, BUFF_SZ);
}
} IO;
u128 rd_from_str(const char *s)
{
int len = strlen(s);
u128 k = 0;
for (int i = 0; i < len; ++i)
k = (k << 1) + (k << 3) + (s[i] ^ 48);
return k;
}
void wr(u128 x)
{
if (x > 9)
wr(x / 10);
putchar(x % 10 + '0');
}
// area 2 : GMP Library
#define ONLINE_JUDGE
#ifdef ONLINE_JUDGE
#ifdef __cplusplus
extern "C"
{
#endif
// SPOJ, codechef, atcoder, acwing, openjudge, ideone (64-bit), etc.
const char *gmp_path = "/usr/lib/x86_64-linux-gnu/libgmp.so.10";
void *__libc_dlopen_mode(const char *x, int y);
void *__libc_dlsym(void *x, const char *y);
#define DLOPEN __libc_dlopen_mode
#define DLSYM __libc_dlsym
#ifdef __cplusplus
}
#endif
void *p = DLOPEN(gmp_path, 2);
void *get(const char *name) { return DLSYM(p, name); }
#define D(name) const auto my_##name = (decltype(name) *)get("__g" #name)
D(mpz_init);
D(mpz_init_set_ui);
D(mpz_init_set_si);
D(mpz_init_set);
D(mpz_init_set_str);
D(mpz_clear);
D(mpz_set);
D(mpz_set_ui);
D(mpz_set_si);
D(mpz_swap);
D(mpz_set_str);
D(mpz_get_str);
D(mpz_inp_str);
D(mpz_out_str);
D(mpz_neg);
D(mpz_abs);
D(mpz_add);
D(mpz_add_ui);
D(mpz_sub);
D(mpz_sub_ui);
D(mpz_mul);
D(mpz_mul_ui);
D(mpz_mul_si);
D(mpz_mul_2exp);
D(mpz_tdiv_r_ui);
D(mpz_tdiv_r);
D(mpz_tdiv_q_ui);
D(mpz_tdiv_q);
D(mpz_tdiv_qr);
D(mpz_tdiv_q_2exp);
D(mpz_divexact);
D(mpz_divexact_ui);
D(mpz_pow_ui);
D(mpz_sqrt);
D(mpz_xor);
D(mpz_and);
D(mpz_ior);
D(mpz_cmp);
D(mpz_sizeinbase);
D(mpz_popcount);
D(mpz_gcd);
D(mpz_probab_prime_p);
D(mpz_nextprime);
D(mpz_fac_ui);
D(mpz_root);
D(mpz_scan0);
D(mpz_scan1);
#undef D
#else
#define my_mpz_init mpz_init
#define my_mpz_init_set_ui mpz_init_set_ui
#define my_mpz_init_set_si mpz_init_set_si
#define my_mpz_init_set mpz_init_set
#define my_mpz_init_set_str mpz_init_set_str
#define my_mpz_clear mpz_clear
#define my_mpz_set mpz_set
#define my_mpz_set_ui mpz_set_ui
#define my_mpz_set_si mpz_set_si
#define my_mpz_swap mpz_swap
#define my_mpz_set_str mpz_set_str
#define my_mpz_get_str mpz_get_str
#define my_mpz_inp_str mpz_inp_str
#define my_mpz_out_str mpz_out_str
#define my_mpz_neg mpz_neg
#define my_mpz_abs mpz_abs
#define my_mpz_add mpz_add
#define my_mpz_add_ui mpz_add_ui
#define my_mpz_sub mpz_sub
#define my_mpz_sub_ui mpz_sub_ui
#define my_mpz_sub_si mpz_sub_si
#define my_mpz_mul mpz_mul
#define my_mpz_mul_ui mpz_mul_ui
#define my_mpz_mul_si mpz_mul_si
#define my_mpz_mul_2exp mpz_mul_2exp
#define my_mpz_tdiv_r_ui mpz_tdiv_r_ui
#define my_mpz_tdiv_r mpz_tdiv_r
#define my_mpz_tdiv_q_ui mpz_tdiv_q_ui
#define my_mpz_tdiv_q mpz_tdiv_q
#define my_mpz_tdiv_qr mpz_tdiv_qr
#define my_mpz_tdiv_q_2exp mpz_tdiv_q_2exp
#define my_mpz_divexact mpz_divexact
#define my_mpz_divexact_ui mpz_divexact_ui
#define my_mpz_pow_ui mpz_pow_ui
#define my_mpz_sqrt mpz_sqrt
#define my_mpz_xor mpz_xor
#define my_mpz_and mpz_and
#define my_mpz_ior mpz_ior
#define my_mpz_cmp mpz_cmp
#define my_mpz_sizeinbase mpz_sizeinbase
#define my_mpz_popcount mpz_popcount
#define my_mpz_gcd mpz_gcd
#define my_mpz_probab_prime_p mpz_probab_prime_p
#define my_mpz_nextprime mpz_nextprime
#define my_mpz_fac_ui mpz_fac_ui
#define my_mpz_root
#define my_mpz_scan0 mpz_scan0
#define my_mpz_scan1 mpz_scan1
#endif
// radix : 10
struct BigInteger_GMP
{
mpz_t n;
// 1.consturct and set
BigInteger_GMP() { my_mpz_init(n); }
BigInteger_GMP(long x) { my_mpz_init_set_si(n, x); }
BigInteger_GMP(std::string s) { my_mpz_init_set_str(n, s.c_str(), 10); }
BigInteger_GMP(const BigInteger_GMP &o) { my_mpz_init_set(n, o.n); }
~BigInteger_GMP() { my_mpz_clear(n); }
void set(const BigInteger_GMP &o) { my_mpz_set(n, o.n); }
void set_si(long x) { my_mpz_set_si(n, x); }
// 2.input and output
// input from fp, and set gmp
void input(FILE *fp = stdin, int base = 10) { my_mpz_inp_str(n, fp, base); }
// set gmp by str
void set_str(char *str, int base = 10) { my_mpz_set_str(n, str, base); }
// output gmp
void print(FILE *fp = stdout) const { my_mpz_out_str(fp, 10, n); }
void println(FILE *fp = stdout) const { my_mpz_out_str(fp, 10, n), fputc('\n', fp); }
// get str from gmp
void get_str(char *str, int base = 10) const { my_mpz_get_str(str, base, n); }
u128 output_u128() const
{
char s[60];
memset(s, 0, sizeof(s));
get_str(s, 10);
return rd_from_str(s);
}
// 3.arithmetics
void operator=(const BigInteger_GMP &o) { my_mpz_set(n, o.n); }
bool operator==(const BigInteger_GMP &o) const { return my_mpz_cmp(n, o.n) == 0; }
bool operator!=(const BigInteger_GMP &o) const { return my_mpz_cmp(n, o.n) != 0; }
bool operator<(const BigInteger_GMP &o) const { return my_mpz_cmp(n, o.n) < 0; }
bool operator>(const BigInteger_GMP &o) const { return my_mpz_cmp(n, o.n) > 0; }
bool operator<=(const BigInteger_GMP &o) const { return my_mpz_cmp(n, o.n) <= 0; }
bool operator>=(const BigInteger_GMP &o) const { return my_mpz_cmp(n, o.n) >= 0; }
friend BigInteger_GMP abs(const BigInteger_GMP &o)
{
BigInteger_GMP ret;
my_mpz_abs(ret.n, o.n);
return ret;
}
friend BigInteger_GMP neg(const BigInteger_GMP &o)
{
BigInteger_GMP ret;
my_mpz_neg(ret.n, o.n);
return ret;
}
BigInteger_GMP operator+(const BigInteger_GMP &o) const
{
BigInteger_GMP ret;
my_mpz_add(ret.n, n, o.n);
return ret;
}
BigInteger_GMP operator+(const unsigned long &o) const
{
BigInteger_GMP ret;
my_mpz_add_ui(ret.n, n, o);
return ret;
}
BigInteger_GMP operator-(const BigInteger_GMP &o) const
{
BigInteger_GMP ret;
my_mpz_sub(ret.n, n, o.n);
return ret;
}
BigInteger_GMP operator-(const unsigned long &o) const
{
BigInteger_GMP ret;
my_mpz_sub_ui(ret.n, n, o);
return ret;
}
BigInteger_GMP operator*(const long &o) const
{
BigInteger_GMP ret;
my_mpz_mul_si(ret.n, n, o);
return ret;
}
BigInteger_GMP operator*(const BigInteger_GMP &o) const
{
BigInteger_GMP ret;
my_mpz_mul(ret.n, n, o.n);
return ret;
}
BigInteger_GMP operator/(const unsigned long &o) const
{
BigInteger_GMP ret;
my_mpz_tdiv_q_ui(ret.n, n, o);
return ret;
}
BigInteger_GMP operator/(const BigInteger_GMP &o) const
{
BigInteger_GMP ret;
my_mpz_tdiv_q(ret.n, n, o.n);
return ret;
}
// mpz_divexact can only user when a is divided by b
BigInteger_GMP divide_exact(const BigInteger_GMP& o) const
{
BigInteger_GMP ret;
my_mpz_divexact(ret.n, n, o.n);
return ret;
}
BigInteger_GMP operator%(const unsigned long &o) const
{
BigInteger_GMP ret;
my_mpz_tdiv_r_ui(ret.n, n, o);
return ret;
}
BigInteger_GMP operator%(const BigInteger_GMP &o) const
{
BigInteger_GMP ret;
my_mpz_tdiv_r(ret.n, n, o.n);
return ret;
}
void div_rem(const BigInteger_GMP &o, BigInteger_GMP &ret_div, BigInteger_GMP &ret_rem)
{
my_mpz_tdiv_qr(ret_div.n, ret_rem.n, n, o.n);
}
BigInteger_GMP operator<<(unsigned long x) const
{
BigInteger_GMP ret;
my_mpz_mul_2exp(ret.n, n, x);
return ret;
}
BigInteger_GMP operator>>(unsigned long x) const
{
BigInteger_GMP ret;
my_mpz_tdiv_q_2exp(ret.n, n, x);
return ret;
}
BigInteger_GMP operator^(unsigned long p) const
{
BigInteger_GMP ret;
my_mpz_pow_ui(ret.n, n, p);
return ret;
}
BigInteger_GMP &operator+=(const BigInteger_GMP &o) { return (*this) = (*this) + o, (*this); }
BigInteger_GMP &operator-=(const BigInteger_GMP &o) { return (*this) = (*this) - o, (*this); }
BigInteger_GMP &operator*=(const BigInteger_GMP &o) { return (*this) = (*this) * o, (*this); }
BigInteger_GMP &operator/=(const BigInteger_GMP &o) { return (*this) = (*this) / o, (*this); }
BigInteger_GMP &operator%=(const BigInteger_GMP &o) { return (*this) = (*this) % o, (*this); }
BigInteger_GMP &operator<<=(unsigned long x) { return (*this) = (*this) << x, (*this); }
BigInteger_GMP &operator>>=(unsigned long x) { return (*this) = (*this) >> x, (*this); }
BigInteger_GMP &operator^=(unsigned long p) { return (*this) = (*this) ^ p, (*this); }
size_t log(int k) const { return ((*this) == BigInteger_GMP(1)) ? 0 : my_mpz_sizeinbase(n, k); }
BigInteger_GMP root(int k) const
{
BigInteger_GMP ret;
my_mpz_root(ret.n, n, k);
return ret;
}
friend BigInteger_GMP gcd(BigInteger_GMP a, BigInteger_GMP b)
{
BigInteger_GMP ret;
my_mpz_gcd(ret.n, a.n, b.n);
return ret;
}
bool is_prime() const { return my_mpz_probab_prime_p(n, 25); }
BigInteger_GMP next_prime() const
{
BigInteger_GMP ret;
my_mpz_nextprime(ret.n, n);
return ret;
}
void factorial(unsigned long x) { my_mpz_fac_ui(n, x); }
// scan from low to high
unsigned long scan_first_r_zero(unsigned long start = 0) const { return my_mpz_scan0(n, start); }
unsigned long scan_first_r_one(unsigned long start = 0) const { return my_mpz_scan1(n, start); }
};
#define rgall(arr) (arr).begin(),(arr).end()
#define rgo1(arr,cnt) (arr).begin()+1,(arr).begin()+1+(cnt)
#define rgcnt(arr,cnt) (arr).begin(),(arr).begin()+(cnt)
#define rgany(arr,rgl,rgr) (arr).begin()+(rgl),(arr).begin()+(rgr)
#define fori(i,a,b) for(int i=(a);i<=(b);i++)
#define ford(i,a,b) for(int i=(a);i>=(b);i--)
#define fori0(i,a,b) for(int i=(a);i<(b);i++)
#define ford0(i,a,b) for(int i=(a);i>(b);i--)
#define fr first
#define sc second
using namespace std;
typedef BigInteger_GMP intinf;
struct pairiinf
{
intinf a,b;
};
struct pairi128
{
__int128 a,b;
};
pairi128 fib128(long long n,const long long& p)
{
if(!n)
return (pairi128){0,1};
if(n&1)
{
auto c=fib128(n-1,p);
return (pairi128){(c.a+c.b)%p,c.a};
}
auto c=fib128(n>>1,p);
return (pairi128){c.a*(c.b*2+c.a)%p,(c.a*c.a+c.b*c.b)%p};
}
pairiinf fib(const intinf& n,const intinf& p)
{
intinf a=0,b=1;
bool flag=false;
const auto& m=n.n[0];
ford(i,m._mp_size-1,0)
ford(j,63,0)
{
if(flag)
{
auto d=a*a+b*b;
(a*=(a+b+b))%=p,b=d%p;
}
if(m._mp_d[i]&1ull<<j)
{
flag=true;
b+=a,swap(a,b);
}
}
return {a,b};
}
int solve_i128(long long p)
{
long long p0=((p+1)%5<3?p-1:(p+1)*2);
if(p<6)
return p-1;
while(!(p0&1))
p0=p0/2;
auto c=fib128(p0,p);
if(c.a==0)
return 4;
else if((c.b*2+c.a)%p==0)
return 1;
else
return 2;
}
int main(int argc,char* argv[],char* envp[])
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
intinf maxp(string(97,'9')+"203"),pow18("1000000000000000000");//10**100-797
cin>>argc;
string ps;
while(argc--)
{
cin>>ps;
intinf p(ps),p0;
if(p<pow18)
{
cout<<solve_i128(stoll(ps))<<'\n';
continue;
}
p0=((p+1)%5<3?p-1:p+p+2);
while(p0%2==0)
p0>>=1;
pairiinf c=fib(p0,p);
if(c.a==0||c.a==p)
cout<<4;
else if(c.a*(c.b*2+c.a)%p==0)
cout<<1;
else
cout<<2;
cout<<'\n';
}
return 0;
}