[zoj4058] [2018ACM青島站·A] Sequence and Sequence - 高精度 - 數學

傳送門:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5815&HPPROTID=f0b51c92

個人第一場ACM,雖然6題Ag滾粗但還是非常值得回味的。

雖然預料到會有高精度題,但是題目的瘋狂程度還是超乎想象(當然沒有隊伍現場ac了)。

題目大意:定義p序列爲{1,1,2,2,2,3,3,3,3……}即連續2個1,連續3個2,連續4個3……,再定義q序列爲:

q1 = 1,q(n) = q(n - 1) + q(pi)

t(約1e4)組詢問,給定n(<=1e40),求q(n)。

看到巨大的n,顯然的想法是如何將其變成一個可以接受的範圍。

第一步還是比較容易想到的:

q(n)=sigma i=1~n q(pi)

然後將q(pi)用同樣的方式展開:

sigma i=1~n sigma j=1~pi q(pj)

分析p序列的性質,發現pi=最大的j使得j*(j+1)/2<=i。

所以,對於某一個q(pj),會在i=j*(j+1)/2~n被計算貢獻

sigma j=1~pn q(pj) * (n - (j*(j+1)/2 - 1))

=n*sigma j=1~pn q(pj) - sigma j=1~pn q(pj)*(j*(j+1)/2-1)

到這一步爲止,我們成功地把1~n變成了1~pn。

由於pn是O(sqrtn)級別,看起來只需要不停地遞歸迭代下去,迭代4次就能變成幾百左右(實際上p(p(p(pn)))=605)。

但這就結束了嗎?

觀察上式,我們發現第一部分確實是可以直接遞歸的,然而第二部分的每一項還需要乘一個跟j有關的多項式。

怎麼辦?不急,我們繼續推式子。

假設我們要求的是sigma i=1~n q(pi)*f(i),其中f(i)是某個多項式。

仍然沿用之前的推法,我們能夠得到:

記sn = sigma i=1~n fi爲f的前綴和。

sn * sigma i=1~pn q(pi) - sigma i=1~pn q(pi)*s(i*(i+1)/2-1)

這個式子與之前的式子形式很類似。實際上,如果把之前的式子看做是q(pi)*1的話,兩者是相同的。

所以我們可以進行如下操作:

定義f(0,n)=1,f(i,n)=s(i-1,n*(n+1)/2-1),s(i,n)=sigma j=1~n f(j,i)

再定義ans(i,n)=sigma j=1~pn q(pj)*f(i,j),則ans(0,n)即爲所求

且ans(i,n)=s(i,n)*ans(0,pn) - ans(i+1,pn)

我們只需要用插值等方法把i=0~3時的f(i,n)和s(i,n)算出來(直接保留多項式的形式,以便計算s(i,n)),再對j=1~605預處理出ans(i,j),然後每次詢問時遞歸下去即可。

還有一些常數優化技巧,比如可以事先打出多項式的係數然後直接在程序裏打表(這樣多項式的係數可以直接開ll),以及每次預處理出所有可能用到的pn和s(i,n)值等。

當然寫起來巨麻煩無比,需要手寫高精度和多項式類,兩者需要實現的東西也很多,因此誕生了我有史以來的第一個1000+行的程序!

#include<bits/stdc++.h>
using namespace std;
#define gc getchar()
#define pc putchar
#define li long long
inline li ll_in(){
	li x = 0,y = 0,c = getchar();
	while(!isdigit(c)) y = c,c = getchar();
	while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ '0'),c = getchar();
	return y == '-' ? -x : x;
}
inline void ll_out(li q){
	if(q < 0){
		putchar('-');
		q = -q;
	} 
	if(q >= 10) ll_out(q / 10);
	putchar(q % 10 + '0');
}

/*****************************以下爲高精度***************************/
const li MAX_PER_UNIT = 100000000;
struct gjd{
	vector<li> a;//存儲高精度數據 
	int len;//該數據的位數 
	bool fh;//符號,0爲非負1爲負 
	gjd(){//初始化(清零)  
		len = fh = 0;a.clear();
	}
	inline gjd operator = (li x){
		a.clear();len = fh = 0;
		if(x < 0){
			fh = 1;x = -x;
		}
		while(x){
			a.push_back(x % MAX_PER_UNIT);
			x /= MAX_PER_UNIT;
		}
		len = a.size();
		return *this;
	}
	inline gjd operator = (string c){
		a.clear();len = fh = 0;
		if(c.size() == 1 && c[0] == '0') return *this;
		int q = MAX_PER_UNIT,j = 0,d = 0;
		while(j < c.size() && (c[j] < '0' || c[j] > '9')) d = c[j++];
		while(j < c.size() && c[j] == '0') ++j;
		if(j == c.size()) return *this;
		for(int i = c.size() - 1;i >= j;--i){
			if(q == MAX_PER_UNIT){
				a.push_back(c[i] - '0');
				++len;
				q = 10;
			}
			else{
				a[len - 1] += (c[i] - '0') * q;
				q *= 10;
			} 
		}
		if(len && d == '-') fh = 1;
		return *this;
	}
	inline gjd operator = (char *c){
		a.clear();len = fh = 0;
		int tp = strlen(c);
		if(tp == 1 && c[0] == '0') return *this;
		int q = MAX_PER_UNIT,j = 0,d = 0;
		while(j < tp && (c[j] < '0' || c[j] > '9')) d = c[j++];
		while(j < tp && c[j] == '0') ++j;
		if(j == tp) return *this;
		for(int i = tp - 1;i >= j;--i){
			if(q == MAX_PER_UNIT){
				a.push_back(c[i] - '0');
				++len;
				q = 10;
			}
			else{
				a[len - 1] += (c[i] - '0') * q;
				q *= 10;
			} 
		}
		if(len && d == '-') fh = 1;
		return *this;
	}
	inline void read(){//讀入 
		a.clear();len = fh = 0;
		vector<char> c;
		char d,e = 0;
		int i = 0;
		d = getchar();
		while(d < '0' || d > '9') e = d,d = getchar();//屏蔽非數字字符 
		while(d == '0') d = getchar();//屏蔽前導0 
		if(d == -1) return;//處理讀入0 
		while(d >= '0' && d <= '9') ++i,c.push_back(d),d = getchar();
		int q = MAX_PER_UNIT;
		for(--i;i >= 0;--i){
			if(q == MAX_PER_UNIT){
				a.push_back(c[i] - '0');
				++len;
				q = 10;
			}
			else{
				a[len - 1] += (c[i] - '0') * q;
				q *= 10;
			} 
		}
		if(len && e == '-') fh = 1;
	}
	inline void print(){//輸出 
		int i = len;
		if(i == 0){
			putchar('0');
			return;
		} 
		if(fh) putchar('-');
		ll_out(a[i - 1]);
		for(i -= 2;i >= 0;--i){
			if(a[i] < 10000000) putchar('0');
			if(a[i] < 1000000) putchar('0');
			if(a[i] < 100000) putchar('0');
			if(a[i] < 10000) putchar('0');
			if(a[i] < 1000) putchar('0');
			if(a[i] < 100) putchar('0');
			if(a[i] < 10) putchar('0');
			ll_out(a[i]);
		} 
	}
};

inline void clear(gjd &x){//初始化(清零) 
	x.a.clear();
	x.len = 0;
}
inline gjd turn(li x){//將低精轉化爲高精
	gjd y;
	if(x < 0){
		y.fh = 1;x = -x;
	}
	while(x){
		y.a.push_back(x % MAX_PER_UNIT); 
		x /= MAX_PER_UNIT;
	}
	y.len = y.a.size();
	return y;
} 
inline li turn(gjd z){//將高精轉化爲低精(有爆ll風險,慎用) 
	li x = 0;
	for(int i = z.len - 1;i >= 0;--i) x = x * MAX_PER_UNIT + z.a[i];
	return z.fh ? -x : x;
} 
inline void swap(gjd &x,gjd &y){//交換兩個高精度數據 
	gjd t = x;
	x = y;
	y = t;
}
inline gjd abs(gjd x){//高精度數據取絕對值
	x.fh = 0;
	return x;
}
inline gjd operator - (gjd x){//相反數 
	if(x.len) x.fh ^= 1;
	return x;
}

inline int cmp(gjd x,gjd y){//高精與高精比較大小 
	if(x.fh && !y.fh) return -1;
	if(!x.fh && y.fh) return 1;
	int tmp = 1;
	if(x.fh && y.fh) tmp = -1;
	int q = x.len,w = y.len; 
	if(q > w) return tmp;
	if(w > q) return -tmp;
	for(--q;q >= 0;--q){
		if(x.a[q] > y.a[q]) return tmp;
		if(x.a[q] < y.a[q]) return -tmp;
	}
	return 0;
}
inline bool operator == (gjd b,gjd c){
	return cmp(b,c) == 0;
} 
inline bool operator < (gjd b,gjd c){
	return cmp(b,c) == -1;
}
inline bool operator > (gjd b,gjd c){
	return cmp(b,c) == 1;
}
inline bool operator <= (gjd b,gjd c){
	return cmp(b,c) != 1;
}
inline bool operator >= (gjd b,gjd c){
	return cmp(b,c) != -1;
}
inline bool operator != (gjd b,gjd c){
	return cmp(b,c) != 0;
}

inline int cmp(gjd x,li b){//高精與低精比較大小 
	return cmp(x,turn(b));
}
inline bool operator == (gjd b,li c){
	return cmp(b,c) == 0;
} 
inline bool operator < (gjd b,li c){
	return cmp(b,c) == -1;
}
inline bool operator > (gjd b,li c){
	return cmp(b,c) == 1;
}
inline bool operator <= (gjd b,li c){
	return cmp(b,c) != 1;
}
inline bool operator >= (gjd b,li c){
	return cmp(b,c) != -1;
}
inline bool operator != (gjd b,li c){
	return cmp(b,c) != 0;
}

inline int cmp(li b,gjd c){//低精與高精比較大小
	return cmp(c,b);
} 
inline bool operator == (li b,gjd c){
	return cmp(b,c) == 0;
} 
inline bool operator < (li b,gjd c){
	return cmp(b,c) == -1;
}
inline bool operator > (li b,gjd c){
	return cmp(b,c) == 1;
}
inline bool operator <= (li b,gjd c){
	return cmp(b,c) != 1;
}
inline bool operator >= (li b,gjd c){
	return cmp(b,c) != -1;
}
inline bool operator != (li b,gjd c){
	return cmp(b,c) != 0;
}

inline gjd max(gjd x,gjd y){//高精與高精取最大值 
	return x > y ? x : y;
}
inline gjd max(gjd x,li y){//高精與低精取最大值 
	return x >= y ? x : turn(y);
}
inline gjd max(li x,gjd y){//低精與高精取最大值 
	return x > y ? turn(x) : y;
}
inline gjd min(gjd x,gjd y){//高精與高精取最小值 
	return x < y ? x : y;
}
inline gjd min(gjd x,li y){//高精與低精取最小值 
	return x <= y ? x : turn(y);
}
inline gjd min(li x,gjd y){//低精與高精取最小值 
	return x < y ? turn(x) : y;
}


inline gjd jia(gjd b,gjd c) {//高精加高精的絕對值相加,要求b,c>=0 
	if(c.len == 0) return b;
	if(b.len == 0) return c;
	int z = 0;
	gjd d;
	int l1 = b.len,l2 = c.len,t1,t2;
	d.a.resize(max(l1,l2) + 1);
	while(z < l1 || z < l2){
		if(l1 > z) t1 = b.a[z];
		else t1 = 0;
		if(l2 > z) t2 = c.a[z];
		else t2 = 0;
		d.a[z] += t1 + t2;
		if(d.a[z] >= MAX_PER_UNIT) d.a[z + 1] = 1,d.a[z] -= MAX_PER_UNIT;
		++z;
	}
	d.len = d.a.size();
	while(d.len && !d.a[d.len - 1]) --d.len;
	return d;
}
inline gjd jia(gjd b,li c){//高精加低精的絕對值相加,要求b,c>=0 
	if(c == 0) return b;
	if(b.len == 0) return turn(c);
	if(c > 9000000000000000000ll) return jia(b,turn(c));//不然會爆ll 
	int z = 0;
	b.a[0] += c;
	while(b.a[z] >= MAX_PER_UNIT){
		if(b.a.size() == z + 1) b.a.push_back(b.a[z] / MAX_PER_UNIT);
		else b.a[z + 1] += b.a[z] / MAX_PER_UNIT;
		b.a[z] %= MAX_PER_UNIT;
		++z;
	}
	b.len = max(b.len,z + 1);
	return b;
}

inline gjd jian(gjd z,li x){//高精減低精的絕對值相減,要求z>=x>=0 
	if(x == 0) return z;
	int q = 0,w = -1;
	z.a[0] -= x;
	while(z.a[q] < 0){
		z.a[q + 1] += z.a[q] / MAX_PER_UNIT;
		z.a[q] %= MAX_PER_UNIT;
		if(z.a[q] < 0){
			z.a[q] += MAX_PER_UNIT;
			--z.a[q + 1];	
		}
		if(z.a[q]) w = q;
		++q;
	}
	if(z.len == q + 1 && z.a[q] == 0) z.len = w + 1;
	return z;
}
inline gjd jian(gjd b,gjd c){//高精減高精的絕對值相減,要求b>=c>=0 
	if(c.len == 0) return b;
	int z = 0,w = -1;
	int l1 = b.len,l2 = c.len;
	gjd d;
	d.a.resize(b.len);
	int tp;
	while(z < l1){
		if(l2 > z) tp = c.a[z];
		else tp = 0;
		d.a[z] += b.a[z] - tp;
		if(d.a[z] < 0){
			d.a[z] += MAX_PER_UNIT;
			d.a[z + 1] = -1;
		}
		if(d.a[z]) w = z;
		++z;
	}
	d.len = w + 1;
	return d;
}

inline gjd operator + (gjd z,li x){//高精加低精 
	if(!z.fh && x >= 0) return jia(z,x);
	gjd a;
	if(z.fh && x < 0){
		z.fh = 0;x = -x;
		a = jia(z,x);
		a.fh = 1;
		return a;
	}
	if(!z.fh && x < 0){
		x = -x;
		if(z >= x){
			a = jian(z,x);
			a.fh = 0;
		}
		else{
			a = jian(turn(x),z);
			a.fh = 1;
		}
		return a;
	}
	if(z.fh && x >= 0){
		z.fh = 0;
		if(z > x){
			a = jian(z,x);
			a.fh = 1;
		}
		else{
			a = jian(turn(x),z);
			a.fh = 0;
		}
		return a;
	}
	return a;
}
inline gjd operator + (gjd z,gjd x){//高精加高精 
	if(!z.fh && !x.fh) return jia(z,x);
	if(z > x) swap(z,x);
	gjd a;
	if(z.fh && x.fh){
		z.fh = x.fh = 0;
		a = jia(z,x);
		a.fh = 1;
		return a;
	}
	if(z.fh && !x.fh){
		z.fh = 0;
		if(z > x){
			a = jian(z,x);
			a.fh = 1;
		}
		else{
			a = jian(x,z);
			a.fh = 0;
		}
		return a;
	}
	return a;
}
inline gjd operator + (li z,gjd x){//低精加高精 
	return x + z;
}
inline gjd operator += (gjd &z,li x){
	z = z + x;
	return z;
}
inline gjd operator += (gjd &z,gjd x){
	z = z + x;
	return z;
}
inline li operator += (li &z,gjd x){//請注意爆long long風險 
	z = z + turn(x);
	return z;
} 
inline gjd operator ++ (gjd &z){//由於特殊原因,使用時只能寫作++z,不能寫作z++ 
	z = z + 1;
	return z;
}

inline gjd operator - (gjd z,li x){//高精減低精 
	return z + (-x);
}
inline gjd operator - (gjd z,gjd x){//高精減高精 
	if(x.len) x.fh ^= 1;
	return z + x;
}
inline gjd operator - (li b,gjd c){//低精減高精
	return turn(b) - c; 
} 
inline gjd operator -= (gjd &z,li x){
	z = z - x;
	return z;
}
inline gjd operator -= (gjd &z,gjd x){
	z = z - x;
	return z;
}  
inline li operator -= (li &z,gjd x){//請注意爆long long風險 
	z = z - turn(x);
	return z;
} 
inline gjd operator -- (gjd &z){//由於特殊原因,使用時只能寫作--z,不能寫作z-- 
	z = z - 1;
	return z;
}

inline gjd operator * (gjd,gjd); 
inline gjd operator * (gjd b,li c){//高精乘低精 
	if(b.len == 0 || c == 0) return turn(0);
	if(c > 10000000000ll) return b * turn(c);//不然會爆long long
	int l = b.len,i,j;
	j = b.fh ^ (c < 0);
	b.fh = 0;c = abs(c);
	for(i = 0;i < l;++i) b.a[i] *= c;
	for(i = 0;i < l - 1;++i){
		b.a[i + 1] += b.a[i] / MAX_PER_UNIT;
		b.a[i] %= MAX_PER_UNIT;
	}
	while(b.a[i] >= MAX_PER_UNIT){
		if(b.a.size() == i + 1) b.a.push_back(b.a[i] / MAX_PER_UNIT);
		else b.a[i + 1] = b.a[i] / MAX_PER_UNIT;
		b.a[i] %= MAX_PER_UNIT;
		++i;
	}
	b.len = i + 1;
	b.fh = j;
	return b;
}
inline gjd operator * (gjd b,gjd c){//高精乘高精 
	if(b.len == 0 || c.len == 0) return turn(0);
	if(min(b.len,c.len) <= 200){
		gjd d;
		d.fh = b.fh ^ c.fh;
		b.fh = c.fh = 0;
		int i,j;
		int l1 = b.len,l2 = c.len;
		d.a.resize(l1 + l2 - 1);
		for(i = 0;i < l1;++i){
			for(j = 0;j < l2;++j){
				d.a[i + j] += b.a[i] * c.a[j];
			}
		}
		for(i = 0;i < l1 + l2 - 2;++i){
			d.a[i + 1] += d.a[i] / MAX_PER_UNIT;
			d.a[i] %= MAX_PER_UNIT;
		}
		while(d.a[i] >= MAX_PER_UNIT){
			if(d.a.size() == i + 1) d.a.push_back(d.a[i] / MAX_PER_UNIT);
			else d.a[i + 1] = d.a[i] / MAX_PER_UNIT;
			d.a[i] %= MAX_PER_UNIT;
			++i; 
		}
		d.len = i + 1;
		return d;
	}
	int l = max(b.len,c.len) / 2;
	gjd ans,b1,b2,c1,c2,s1,s2,s3,s4,s5;
	int tmp = b.fh ^ c.fh;
	b.fh = c.fh = 0;
	int i;
	b1.len = min(l,b.len);
	b1.a.resize(b1.len);
	for(i = 0;i < l && i < b.len;++i) b1.a[i] = b.a[i];
	c1.len = min(l,c.len);
	c1.a.resize(c1.len);
	for(i = 0;i < l && i < c.len;++i) c1.a[i] = c.a[i];
	if(b.len > l){
		b2.a.resize(b.len - 1);
		for(i = l;i < b.len;++i) b2.a[i - l] = b.a[i];
		b2.len = b.len - l;
	}
	if(c.len > l){
		c2.a.resize(c.len - 1);
		for(i = l;i < c.len;++i) c2.a[i - l] = c.a[i];
		c2.len = c.len - l;
	}
	while(b1.len && !b1.a[b1.len - 1]) --b1.len;
	while(c1.len && !c1.a[c1.len - 1]) --c1.len;
	while(b2.len && !b2.a[b2.len - 1]) --b2.len;
	while(c2.len && !c2.a[c2.len - 1]) --c2.len;
	s1 = b1 * c1;
	s2 = b2 * c2;
	s3 = (b1 + b2) * (c1 + c2) - s1 - s2;
	if(s2.len){
		s4.a.resize((l << 1) + s2.len);
		for(i = 0;i < s2.len;++i) s4.a[i + (l << 1)] = s2.a[i];
		s4.len = (l << 1) + s2.len; 
	}
	if(s3.len){
		s5.a.resize(l + s3.len);
		for(i = 0;i < s3.len;++i) s5.a[i + l] = s3.a[i];
		s5.len = l + s3.len;
	}
	ans = s1 + s4 + s5;ans.fh = tmp;
	return ans;
}
inline gjd operator * (li z,gjd x){//低精乘高精 
	return x * z;
} 
inline gjd operator *= (gjd &z,li x){
	z = z * x;
	return z;
}
inline gjd operator *= (gjd &z,gjd x){
	z = z * x;
	return z;
}  
inline li operator *= (li &z,gjd x){//請注意爆long long風險 
	z = z * turn(x);
	return z;
} 

inline gjd operator / (gjd b,li c){//高精除以低精 
	if(!c){
		puts("error:divided by zero!");
		exit(1);
	}
	if(b.len == 0) return turn(0);
	gjd q;
	q.fh = b.fh ^ (c < 0);
	b.fh = 0;c = abs(c);
	int l = b.len - 1;
	int x = 0,y;
	while(l){
		y = b.a[l] / c;
		if(!x && y){
			x = l;
			q.a.resize(x);
			q.a.push_back(y);
		} 
		if(x) q.a[l] = y;
		b.a[l - 1] += b.a[l] % c * MAX_PER_UNIT;
		--l;
	}
	if(!x){
		y = b.a[0] / c;
		if(!y) return q;
		q.a.push_back(y);
		q.len = x + 1;
		return q;
	}
	q.a[0] = b.a[0] / c;
	if(!q.a[0] && !x) q.len = 0;
	else q.len = x + 1;
	return q;
}
inline gjd operator / (gjd b,gjd c){//高精除以高精 
	if(c.len == 0){
		puts("error:divided by zero!");
		exit(1);
	} 
	if(b.len == 0) return turn(0);
	int tmp = b.fh ^ c.fh;
	b.fh = c.fh = 0;
	if(b < c) return turn(0);
	gjd q,w;//q:商;w:餘數 
	int i = b.len - 1,l = -1,al,ar,am;
	for(;i >= 0;i--){
		w = w * MAX_PER_UNIT + b.a[i];
		if(w >= c){
			if(l == -1) q.a.resize(i + 1);
			l = max(l,i);
			al = 1,ar = MAX_PER_UNIT - 1;
			while(al <= ar){
				am = al + ar >> 1;
				if(w >= c * am){
					q.a[i] = am;
					al = am + 1;
				} 
				else ar = am - 1;
			}
			w -= c * q.a[i];
		}
	} 
	q.len = l + 1;
	q.fh = tmp;
	return q;
}
inline gjd operator / (li x,gjd y){//低精除以高精 
	return turn(x) / y;
}
inline gjd operator /= (gjd &z,li x){
	z = z / x;
	return z;
}
inline gjd operator /= (gjd &z,gjd x){
	z = z / x;
	return z;
}  
inline li operator /= (li &z,gjd x){//請注意爆long long風險 
	if(z < x){
		z = 0;
		return z;
	}
	z = z / turn(x);
	return z;
} 


inline gjd operator % (gjd b,li c){//高精模低精 
	if(!c){
		puts("error:divided by zero!");
		exit(1);
	}
	if(b.len == 0) return turn(0);
	int l = b.len - 1,tmp = b.fh;
	b.fh = 0;c = abs(c);
	while(l){
		b.a[l - 1] += b.a[l] % c * MAX_PER_UNIT;
		--l;
	}
	b.a[0] %= c;
	gjd y = turn(b.a[0]);
	y.fh = tmp;
	return y;
}
inline gjd operator % (gjd b,gjd c){//高精模高精 
	if(c.len == 0){
		puts("error:divided by zero!");
		exit(1);
	} 
	if(b.len == 0) return turn(0);
	gjd w,tp;//w:餘數 tp:輔助計算 
	int tmp = b.fh;
	b.fh = c.fh = 0;
	if(b < c){
		b.fh = tmp;
		return b;
	} 
	int i = b.len - 1,j;
	for(;i >= 0;--i){
		w = w * MAX_PER_UNIT + b.a[i];
		j = 1 << 26;
		while(j){
			tp = c * j;
			if(w >= tp) w -= tp;
			j >>= 1;
		}
	} 
	w.fh = tmp;
	return w;
}
inline gjd operator % (li x,gjd y){//低精模高精 
	return turn(x) % y;
}
inline gjd operator %= (gjd &z,li x){
	z = z % x;
	return z;
}
inline gjd operator %= (gjd &z,gjd x){
	z = z % x;
	return z;
}  
inline li operator %= (li &z,gjd x) {//請注意爆long long風險 
	if(z < x) return z;
	z = z % turn(x);
	return z;
}  

inline gjd spw(gjd q,li w){//高精度數據的單精度次冪運算,結果爲高精度,要求指數是非負整數 
	if(w < 0){
		puts("error:in function 'spw(gjd q,long long int w)',w is negative!");
		exit(1);
	}
	if(!w) return turn(1);
	if(w == 1) return q;
	gjd z;
	z = spw(q,w >> 1);
	z = z * z;
	if(w & 1) z = z * q;
	return z;
}
inline gjd spw(li q,li w){//兩個單精度數據的冪運算,結果爲高精度,要求指數是非負整數  
	return spw(turn(q),w);
} 
inline gjd operator ^ (gjd q,li w){
	return spw(q,w);
}
inline gjd operator ^= (gjd &q,li w){
	q = q ^ w;
	return q;
}
//以下爲指數是高精度的冪運算,小心結果可能很大! 
inline gjd spw(gjd q,gjd w){
	return spw(q,turn(w));
}
inline gjd spw(li q,gjd w){
	return spw(turn(q),turn(w));
}
inline gjd operator ^ (gjd q,gjd w){
	return spw(q,turn(w));
}
inline gjd operator ^= (gjd &q,gjd w){
	q = q ^ w;
	return q;
}
inline gjd operator ^ (li q,gjd w){
	return spw(turn(q),turn(w));
}
inline li operator ^= (li &q,gjd w){//小心爆ll的風險 
	q = turn(q ^ w);
	return q;
}

inline gjd sqrt(gjd b){//高精開平方(牛頓迭代法)
	if(b.fh){
		puts("error:in function 'sqrt(gjd b)',b is negative!");
		exit(1);
	}
	if(b.len == 0) return turn(0);
	gjd z[2],tmp;
	bool i = 0; 
	z[1].a.resize((b.len + 1) / 2 - 1);
	z[1].a.push_back(1);
	z[1].len = (b.len + 1) / 2;
	while(z[i] != z[!i]){
		tmp = (b / z[!i] + z[!i]) / 2;
		if(z[!i] > z[i] && z[!i] - z[i] == 1 && z[i] == tmp) break;
		z[i] = tmp;
		i = !i;
	}
	return z[i];
}
inline gjd sqr(gjd a,li b){//二分法高精開b次方根,要求次數爲正整數 
	if(b <= 0){
		puts("error:in function 'sqr(gjd a,li b)',b is not positive!");
		exit(1);
	}
	if(a.fh && b % 2 == 0){
		puts("error:in function 'sqr(gjd a,li b)',a is negative and b is even!");
		exit(1);
	}
	if(a.len == 0) return turn(0);
	if(b == 1) return a;
	int tmp = a.fh;
	a.fh = 0;
	if(a <= b) return turn(1 * tmp); 
	gjd l,r,mid,ans;
	l.a.resize((a.len - 1) / b);r.a.resize((a.len - 1) / b + 1);
	l.a.push_back(1);l.len = (a.len - 1) / b + 1;r.a.push_back(1);r.len = l.len + 1;
	if(b == 2){
		while(l <= r){
			mid = (l + r) / 2;
			if(mid * mid <= a){
				ans = mid;
				l = mid + 1;
			}
			else r = mid - 1;
		}
	}
	else{
		while(l <= r){
			mid = (l + r) / 2;
			if(spw(mid,b) <= a){
				ans = mid;
				l = mid + 1;
			}
			else r = mid - 1;
		}
	}
	ans.fh = tmp;
	return ans;
}


inline gjd gcd(gjd x,gjd y){//高精度與高精度的最大公因數,返回值非負(下同) 
	x.fh = y.fh = 0; 
	if(!x.len) return y;
	if(!y.len) return x;
	gjd q = turn(1);
	if(x < y) swap(x,y);
	while(x != y){
		if((x.a[0] & 1) && (y.a[0] & 1)) x -= y;
		else if(x.a[0] & 1) y /= 2;
		else if(y.a[0] & 1) x /= 2;
		else{
			x /= 2;
			y /= 2;
			q *= 2;
		}
		if(x < y) swap(x,y);
	}
	return q * x;
} 
inline gjd gcd(gjd x,li y){//高精度與單精度的最大公因數 
	x.fh = 0;y = abs(y);
	return y == 0 ? x : gcd(turn(y),x % y);
}
inline gjd gcd(li x,gjd y){//單精度與高精度的最大公因數 
	return gcd(y,x);
}
inline gjd gjd_gcd(li x,li y){//單精度與單精度的最大公因數,結果返回高精 
	x = abs(x);y = abs(y);
	return y == 0 ? turn(x) : gjd_gcd(y,x % y);
}
inline li gcd(li x,li y){//單精度與單精度的最大公因數,結果返回單精 
	x = abs(x);y = abs(y);
	return y == 0 ? x : gcd(y,x % y);
}

inline gjd lcm(gjd x,gjd y){//高精度與高精度的最小公倍數 
	x.fh = y.fh = 0;
	if(!x.len || !y.len) return turn(0);
	return x / gcd(x,y) * y;
}
inline gjd lcm(gjd x,li y){//高精度與單精度的最小公倍數 
	x.fh = 0;y = abs(y);
	if(!x.len || !y) return turn(0);
	return x / gcd(x,y) * y;
} 
inline gjd lcm(li x,gjd y){//單精度與高精度的最小公倍數 
	return lcm(y,x);
}  
inline gjd gjd_lcm(li x,li y){//單精度與單精度的最小公倍數,結果返回高精 
	x = abs(x);y = abs(y);
	if(!x || !y) return turn(0);
	return x / gjd_gcd(x,y) * y;
}
inline li lcm(li x,li y){//單精度與單精度的最小公倍數,結果返回單精 
	x = abs(x);y = abs(y);
	if(!x || !y) return 0;
	return x / gcd(x,y) * y;
}
/*****************************以上爲高精度***************************/

/*****************************以下爲多項式***************************/
const int sz = 17;
struct dxs{
	gjd a[sz];
	gjd div;
	int len;
	dxs(){
		for(int i = 0;i < sz;++i) clear(a[i]);
		div = 1;len = 0;
	}
	void clr(){
		for(int i = 0;i < sz;++i) clear(a[i]);
		div = 1;len = 0;
	}
	void out(){
		ll_out(len);pc('\n');
		for(int i = 0;i <= len;++i){
			a[i].print();pc(' ');
		} 
		pc('\n');div.print();pc('\n');
	}
}a[6],s[6];
void yf(dxs &q){
	if(q.div.fh){
		q.div.fh ^= 1;
		for(int i = 0;i <= q.len;++i) if(q.a[i].len) q.a[i].fh ^= 1;
	}
	gjd g = q.div;
	for(int i = 0;i <= q.len;++i) g = gcd(g,q.a[i]);
	q.div /= g;
	for(int i = 0;i <= q.len;++i) q.a[i] /= g; 
	while(q.len && q.a[q.len] == 0) --q.len;
}
dxs operator + (dxs q,dxs w){
	gjd a = lcm(q.div,w.div);
	gjd b = a / q.div,c = a / w.div;
	dxs ans;
	ans.len = max(q.len,w.len);
	ans.div = a;
	for(int i = 0;i <= ans.len;++i) ans.a[i] = q.a[i] * b + w.a[i] * c;
	yf(ans);
	return ans;
}
dxs operator * (dxs q,dxs w){
	dxs ans;
	ans.len = q.len + w.len;
	ans.div = q.div * w.div;
	for(int i = 0;i <= q.len;++i){
		for(int j = 0;j <= w.len;++j){
			ans.a[i + j] += q.a[i] * w.a[j];
		} 
	} 
	yf(ans);
	return ans;
}
/*****************************以上爲多項式***************************/

/*****************************以下爲預處理部分***************************/
gjd tmp[10010],val[1010][6];
int p1[1010];
inline gjd getp(gjd q){
	gjd ans = sqr(q * 2,2);
	if((ans + 1) * ans > q * 2) --ans;
	return ans;
}
inline gjd getx(dxs q,gjd x){
	gjd ans = q.a[q.len];
	for(int i = q.len - 1;i >= 0;--i) ans = ans * x + q.a[i];
	return ans / q.div;
}
dxs tp2[35];
dxs cheng(int l,int r){
	if(l == r) return tp2[l];
	int mid = l + r >> 1;
	return cheng(l,mid) * cheng(mid + 1,r);
}
inline void wk(dxs q,dxs &w,bool fg,int mx){
	int i,j = 1;
	for(i = 1;i <= mx;++i){
		tmp[i] = tmp[i - 1];
		for(;j <= (!fg ? i * (i + 1) / 2 - 1 : i);++j) tmp[i] += getx(q,turn(j));
	}
	w.clr();
	for(i = 1;i <= mx;++i){
		dxs tp;
		gjd tp3;tp3 = 1;
		tp.a[0] = 1;
		for(j = 1;j <= mx;++j) if(i != j){
			tp2[j].a[1] = 1;tp2[j].a[0] = -j;tp2[j].len = 1;
			tp3 *= i - j;
		}
		tp2[i].clr();tp2[i].a[0] = 1;
		tp = cheng(1,mx);
		tp.div = tp3;
		for(j = 0;j <= tp.len;++j) tp.a[j] *= tmp[i];
		yf(tp);
		w = w + tp;
	}
}

const int mx = 605;
void init(){
	register int i,j;
	p1[1] = 1;
	for(i = 2;i <= mx;++i) p1[i] = turn(getp(turn(i)));
	a[0].a[0] = 1;
	s[0].a[1] = 1;
	s[0].len = 1;
	val[1][0] = 1;
	for(i = 2;i <= mx;++i) val[i][0] = val[i - 1][0] + val[p1[i]][0];
	for(i = 1;i <= 3;++i){
		wk(a[i - 1],a[i],0,pow(2,i + 1) - 1);
		wk(a[i],s[i],1,pow(2,i + 1));
		val[1][i] = getx(a[i],turn(1));
		for(j = 2;j <= mx;++j) val[j][i] = val[j - 1][i] + val[p1[j]][0] * getx(a[i],turn(j));
	}
	//for(i = 0;i <= 3;++i) a[i].out();
	//for(i = 0;i <= 3;++i) s[i].out();
	for(i = 1;i <= mx;++i) val[i][4] = val[i - 1][4] + val[p1[i]][0] * getx(s[3],turn(i * (i + 1) / 2 - 1));
}
/*****************************以上爲預處理部分***************************/

gjd pp[6],ss[6][6];
gjd work(int q,int k){
	if(pp[q] <= mx) return val[turn(pp[q])][k];
	return ss[q][k] * work(q + 1,0) - work(q + 1,k + 1);
}
inline void file(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
}
int main(){
	//file();
	init();
	int t = ll_in();
	while(t--){
		pp[0].read();
		ss[0][0] = pp[0];
		for(int i = 1;i <= 4;++i){
			pp[i] = getp(pp[i - 1]);
			if(i != 4){
				ss[i][0] = pp[i];
				for(int j = 1;j <= i;++j) ss[i][j] = getx(s[j],pp[i]);
			} 
		} 
		work(0,0).print();
		pc('\n');
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章