【题解】ABC293E Sol()

题目大意

给定整数 \(A,X,M\),求 \(\sum\limits^{X-1}_{i=0} A^i\) 对 \(M\) 取模的值。

数据范围:\(1 \le A,M \le 10^9\),\(1 \le X \le 10^{12}\)。

题目分析

直接算显然会 T 飞,所以尝试把这个式子弄成一些比较好求的玩意儿。

手玩一下样例。例如 \(A=8,X=10\) 时,答案就为 \(\sum\limits^{10-1}_{i=0} 8^i=8^0+8^1+\cdots+8^9=(8^0+8^5)(8^0+8^1+8^2+8^3+8^4)=(8^0+8^4)(8^0+8^1+8^2+8^3)+8^8+8^9=\cdots\)。也就是说,我们可以把整个式子分解成关于 \(A\) 的两个多项式的乘积加上若干个 \(A\) 的幂次方的形式,求出这两个多项式的值乘起来并加上剩下几个 \(A^i\) 即可。

但是这样似乎还不能完全解决问题,要是这两个多项式的长度太长或者剩余的 \(A^i\) 太多,照样 T,考虑控制两个多项式的长度和 \(A^i\) 的数量。

于是我们就可以想到一个叫根号的东西。

可以把式子拆成如下形式:\(\sum\limits^{X-1}_{i=0} A^i=(A^{0\times[\sqrt{X}]}+A^{1\times[\sqrt{X}]}+\cdots+A^{([\sqrt{X}]-1)\times[\sqrt{X}]})(A^0+A^1+\cdots+A^{[\sqrt{X}]-1})+A^{[\sqrt{X}]\times[\sqrt{X}]}+A^{[\sqrt{X}]\times[\sqrt{X}]+1}+\cdots+A^X\),比如 \(A=8,X=10\) 时,答案就可表示成 \([(8^0+8^3+8^6)(8^0+8^1+8^2)+8^9]\)。这样,两个多项式的长度和剩余 \(A^i\) 的数量最多也不会超过 \([\sqrt{X}]\),时间复杂度约为 \(O(\sqrt{X}\log X)\),而 \(X\le 10^{12}\),理论上能过。

但理论上能过并不代表它不会 T。这个时间复杂度实际上相当极限,如果用了一些常数比较大的写法的话依然是过不去的。比如快速幂使用递归写法时会 T 掉几个点,而换成循环快速幂就能 1500ms 卡过,不要问我怎么知道的。

(其实出题人完全可以把 \(X\) 开成 \(10^{18}\),把这个解法彻底卡掉,但良心出题人似乎特意把这种做法放过去了?)

代码

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
using namespace std;
ll a,x,MOD,len,ans,sum,sum1;
ll qpow(ll x,ll y) {
	ll mul=1;
	while (y) {
		if (y&1) mul=mul*x%MOD;
		x=x*x%MOD,y>>=1;
	}
	return mul;
}
int main() {
	scanf("%lld%lld%lld",&a,&x,&MOD),a%=MOD;
	len=sqrt(x);
	for (ll i=0;i<len;i++)
		sum=(sum+qpow(a,i))%MOD,sum1=(sum1+qpow(a,i*len))%MOD;
	for (ll i=len*len;i<x;i++) ans=(ans+qpow(a,i))%MOD;
	printf("%lld",(ans+sum*sum1%MOD)%MOD);
	return 0;
}
————————

题目大意

给定整数 \(A,X,M\),求 \(\sum\limits^{X-1}_{i=0} A^i\) 对 \(M\) 取模的值。

数据范围:\(1 \le A,M \le 10^9\),\(1 \le X \le 10^{12}\)。

题目分析

直接算显然会 T 飞,所以尝试把这个式子弄成一些比较好求的玩意儿。

手玩一下样例。例如 \(A=8,X=10\) 时,答案就为 \(\sum\limits^{10-1}_{i=0} 8^i=8^0+8^1+\cdots+8^9=(8^0+8^5)(8^0+8^1+8^2+8^3+8^4)=(8^0+8^4)(8^0+8^1+8^2+8^3)+8^8+8^9=\cdots\)。也就是说,我们可以把整个式子分解成关于 \(A\) 的两个多项式的乘积加上若干个 \(A\) 的幂次方的形式,求出这两个多项式的值乘起来并加上剩下几个 \(A^i\) 即可。

但是这样似乎还不能完全解决问题,要是这两个多项式的长度太长或者剩余的 \(A^i\) 太多,照样 T,考虑控制两个多项式的长度和 \(A^i\) 的数量。

于是我们就可以想到一个叫根号的东西。

可以把式子拆成如下形式:\(\sum\limits^{X-1}_{i=0} A^i=(A^{0\times[\sqrt{X}]}+A^{1\times[\sqrt{X}]}+\cdots+A^{([\sqrt{X}]-1)\times[\sqrt{X}]})(A^0+A^1+\cdots+A^{[\sqrt{X}]-1})+A^{[\sqrt{X}]\times[\sqrt{X}]}+A^{[\sqrt{X}]\times[\sqrt{X}]+1}+\cdots+A^X\),比如 \(A=8,X=10\) 时,答案就可表示成 \([(8^0+8^3+8^6)(8^0+8^1+8^2)+8^9]\)。这样,两个多项式的长度和剩余 \(A^i\) 的数量最多也不会超过 \([\sqrt{X}]\),时间复杂度约为 \(O(\sqrt{X}\log X)\),而 \(X\le 10^{12}\),理论上能过。

但理论上能过并不代表它不会 T。这个时间复杂度实际上相当极限,如果用了一些常数比较大的写法的话依然是过不去的。比如快速幂使用递归写法时会 T 掉几个点,而换成循环快速幂就能 1500ms 卡过,不要问我怎么知道的。

(其实出题人完全可以把 \(X\) 开成 \(10^{18}\),把这个解法彻底卡掉,但良心出题人似乎特意把这种做法放过去了?)

代码

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
using namespace std;
ll a,x,MOD,len,ans,sum,sum1;
ll qpow(ll x,ll y) {
	ll mul=1;
	while (y) {
		if (y&1) mul=mul*x%MOD;
		x=x*x%MOD,y>>=1;
	}
	return mul;
}
int main() {
	scanf("%lld%lld%lld",&a,&x,&MOD),a%=MOD;
	len=sqrt(x);
	for (ll i=0;i<len;i++)
		sum=(sum+qpow(a,i))%MOD,sum1=(sum1+qpow(a,i*len))%MOD;
	for (ll i=len*len;i<x;i++) ans=(ans+qpow(a,i))%MOD;
	printf("%lld",(ans+sum*sum1%MOD)%MOD);
	return 0;
}