【题目描述】
给定正整数 n , p 和非负整数 a , b ,求 an+an-1b+an-2b2+…+a2bn-2+abn-1+bn,例如当n=1时,该结果等于 a+b 。由于答案可能很大,请对 p 取模。
【输入格式】
▲多组数据
第一行,一个正整数T,表示数据组数。
接下来 T 行,每行四个整数 n , a , b , p ,表示一组数据,相邻两个数之间有空格隔开。
【输出格式】
共 T 行,每行一个非负整数,表示一组数据的答案。
【样例输入】
3
2 3 3 233
5 1 2 100
1000 233 666 998244353
【样例输出】
27
63
952904376
【数据规模及约定】
对于30%的数据,满足 T≤10,n,a,b≤106,p=109+7。
对于60%的数据,满足 T≤10,000,n,a,b≤109,p=109+7。
另有10%的数据,满足 a=0。
另有10%的数据,满足 a=1。
对于100%的数据,满足 T≤30,000,n,a,b,p≤1012。
思路:
可以采取分治来求解。
先从简单入手: 如:
当 n=5 时,有: a5+a4b+a3b2+a2b3+ab4+b5 → 可以将序列从中间分为 a5+a4b+a3b2 和 a2b3+ab4+b5。
那么从中提出 a3 和 b3 可得:a3( a2+ab+b2 ) + b3( a2+ab+b2 ) →又可以合并为 ( a3+b3 ) × ( a2+ab+b2 )。
后面的式子还可以继续分治: 可以看作是 n=2 时所要求的式子。
但这时,n 为偶数,式子是一个三项式,无法从中间直接分解。则考虑添项:a2+ab+b2 = a2+ab+ab+b2-ab 。
最终为:(a+b)(a+b)-ab 。
所以 a5+a4b+a3b2+a2b3+ab4+b5 = ( a3+b3 ) × ( (a+b)(a+b)-ab )。
从上述简单的求解可以看出:
当 n 为奇数时,可以将式子直接二分递归下去(dfs(n>>1)),当 n 为奇数时,将式子添项后再二分递归(dfs((n>>1)+1)-ksm((a*b),(n>>1))) 奇数 n 的 -ksm((a*b),(n>>1)) 是减去中间添加的项。
代码实现:
long long dfs(long long k)//k,此时的二分 n 的值
{
if(k==1) return (a+b)%p;
if(k&1) return ksc( ( ksm(a,(k>>1)+1,p)+ksm(b,(k>>1)+1,p) )%p , dfs(k>>1) ,p)%p;// n 为奇数
return ( ksc( ( ksm(a,k>>1,p)+ksm(b,k>>1,p) )%p , dfs(k>>1) ,p)%p + ( p - ksm(ksc(a,b,p),k>>1,p) ) )%p;
}//ksc 为O(1)快速乘,ksm 为快速幂,p 为模数
完整代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<string> #include<cstdlib> #include<stack> #include<vector> #include<queue> #include<deque> #include<map> #include<set> using namespace std; long long T; long long n,a,b,p; inline long long read() { long long kr=1,xs=0; char ls; ls=getchar(); while(!isdigit(ls)) { if(!(ls^45)) kr=-1; ls=getchar(); } while(isdigit(ls)) { xs=(xs<<1)+(xs<<3)+(ls^48); ls=getchar(); } return xs*kr; } inline long long ksc(long long x,long long y,long long mod) { long long tmp=(x*y-(long long)((long double)x/mod*y+1.0e-8)*mod); return tmp<0 ? tmp+mod : tmp; }//O(1)快速乘 inline long long ksm(long long x,long long y,long long mod) { if(y==0) return 1; if(y==1) return x; if(y&1) { long long res=ksm(x,y>>1,mod); res=ksc(res,res,mod); res=ksc(res,x,mod); return res%mod; } else { long long res=ksm(x,y>>1,mod); return res=(ksc(res,res,mod)); } }//快速幂 long long dfs(long long k) { if(k==1) return (a+b)%p; if(k&1) return ksc( ( ksm(a,(k>>1)+1,p)+ksm(b,(k>>1)+1,p) )%p , dfs(k>>1) ,p)%p; return ( ksc( ( ksm(a,k>>1,p)+ksm(b,k>>1,p) )%p , dfs(k>>1) ,p)%p + ( p - ksm(ksc(a,b,p),k>>1,p) ) )%p; }//分治(关键) int main() { freopen("guojia.in","r",stdin); freopen("guojia.out","w",stdout); T=read(); while(T--) { n=read(),a=read(),b=read(),p=read(); if(a==0)//特判 a 为 0 的情况 { printf("%lld ",ksm(b,n,p)); continue; } printf("%lld ",dfs(n));//把 n 丢进递归函数二分求出答案 } return 0; }