国家—(分治)

【题目描述】

给定正整数 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≤106p=109+7。

对于60%的数据,满足 T≤10,000,n,a,b≤109p=109+7。

另有10%的数据,满足 a=0。

另有10%的数据,满足 a=1。

对于100%的数据,满足 T≤30,000,n,a,b,p≤1012

思路:

  可以采取分治来求解。

  先从简单入手: 如:

  当 n=5 时,有: a5+a4b+a3b2+a2b3+ab4+b→ 可以将序列从中间分为 a5+a4b+a3b2  和 a2b3+ab4+b5

  那么从中提出 a3 和 b3 可得:a3( a2+ab+b) + b3( a2+ab+b) →又可以合并为 ( a3+b) × ( a2+ab+b)。

  后面的式子还可以继续分治: 可以看作是 n=2 时所要求的式子。

  但这时,n 为偶数,式子是一个三项式,无法从中间直接分解。则考虑添项:a2+ab+b= a2+ab+ab+b2-ab 。

  最终为:(a+b)(a+b)-ab 。

  所以 a5+a4b+a3b2+a2b3+ab4+b= ( a3+b) × ( (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\n",ksm(b,n,p));
            continue;
        }
        printf("%lld\n",dfs(n));//把 n 丢进递归函数二分求出答案 
    }
return 0;
}

 

posted @ 2018-10-03 12:55  落笔映惆怅丶  阅读(226)  评论(0编辑  收藏  举报