SHOI 2017 相逢是问候

LOJ 2142 相逢是问候

这道题真的长见识

首先 这道题目很轻易地就能想到用 线段树 求和
但是题目的难点在于 caimodpcaimodpmodp
解决这个问题的公式是

ABABmodϕ(p)+[B>=ai]ϕ(p)modp

根据这个公式逆推一下
caimodp=caimodϕ(p)+[ai>ϕ(p)]ϕ(p)modp

(以下简写 Bmodϕ(p)+[B>=ai]ϕ(p) Bmodϕ(p)
ccaimodp=ccaimodϕ(p)modp=ccaimodϕ(ϕ(p))modϕ(p)modp

然后这么一路推下去 我们会发现 当 ϕ...ϕ(p)=1 时 整个数字的值都不会变
(大概意思是 算了这么多次之后 就不算了)
并且根据网上其它大神的证明 这样的次数不会大于 logp
所以说暴力修改每一个点的值 然后线段树求和

时间复杂度(不严格)
O(nlog2ploga)
(最多修改 n 个点 每个点最多 logp 次修改 每次修改最多进行 logp 快速幂 loga

超速幂

我们不能满足于这种有可能会T的时间复杂度
于是强行将 loga 压到常数的 超速幂 就出现了

我们发现 所有的快速幂的底数都是 c 模数都是 ϕ(p) ϕ(ϕ(p))

于是我们就对底数为c 模数为 ϕ(p) ϕ(ϕ(p)) …的 (1ai) 次方项进行预处理
ai 最大是 1e8 大概是 227
所以说 我们拆分 i=a214+b 其中 a=imod214
因为有

ci=ca214+b=ca214cb

所以我们就分部求出 a[0,2141],b[0,2141] 对应的 mod=ϕ(p)ϕ(ϕ(p))... c 的快速幂值

然后每次求幂时 将 指数 拆分成 i=a214+b 然后取出 ab 的值乘一下即可

优越性

算一下吧
ϕ(p) ϕ(ϕ(p)) …最多有 logp
214=16384
预处理的复杂度大概是百万级别的 相比于原来的复杂度可以说时非常小了
并且原先的复杂度降至了 O(nlog2p)
跑出来感觉非常快

附上参考代码



#include 
#include 
#define mid (l+r>>1)
using namespace std;

inline int input()
{
    char c=getchar();int o;bool f=0;
    while(c>57||c<48)f|=(c=='-'),c=getchar();
    for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
    return f?-o:o;
}

int n,c,mod,A[50123];
int cnt=0,phi[50];
int num[30][20000][2];
bool mark[30][20000][2];

int Gphi(int x)
{
    int ans=x;
    for(int i=2;i*i<=x;i++)
        if(x%i==0)
        {
            ans=ans-ans/i;
            while(x%i==0)x/=i;
        }
    if(x>1)ans=ans-ans/x;
    return ans;
}

int KSM(int a,int b,int md,bool &mk)
{
    int ans=1;
    while(b)
    {
        if(b&1)mk|=(1LL*ans*a>=md),ans=1LL*ans*a%md;
        mk|=(1LL*a*a>=md&&(b!=1));
        b>>=1;
        a=1LL*a*a%md;
    }
    return ans;
}

int FSQ(int p,int T,bool &mk)
{
    T=min(T,cnt);mk=0;
    mk|=mark[T][p&16383][0]|mark[T][p>>14][1];
    mk|=(1LL*num[T][p>>14][1]*num[T][p&16383][0]>=phi[T]);
    return 1LL*num[T][p>>14][1]*num[T][p&16383][0]%phi[T];
}


int SQ(int x,int T)
{
    bool MA;
    if(x>=phi[T])x=x%phi[T]+phi[T];
    while(T--)
    {
        x=FSQ(x,T,MA);
        if(MA)x+=phi[T];
    }
    return x%mod;
}

int L,R;
long long v[400123],done[400123];

void BUILD(int o,int l,int r)
{
    if(l==r){v[o]=A[l];done[o]=0;return;}
    int ls=o<<1,rs=o<<1|1;
    BUILD(ls,l,mid);BUILD(rs,mid+1,r);
    v[o]=(v[ls]+v[rs])%mod;
    done[o]=0;
}

void CH(int o,int l,int r)
{
    if(done[o]>=cnt)return;
    if(l==r)
    {
        done[o]++;
        v[o]=SQ(A[l],done[o]);
        return;
    }
    int ls=o<<1,rs=o<<1|1;
    if(L<=mid&&l<=R)CH(ls,l,mid);
    if(L<=r&&mid1,r);
    v[o]=(v[ls]+v[rs])%mod;
    done[o]=min(done[ls],done[rs]);
}

int sum(int o,int l,int r)
{
    if(L<=l&&r<=R)return v[o];
    long long ans=0,ls=o<<1,rs=o<<1|1;
    if(L<=mid&&l<=R)ans+=sum(ls,l,mid);
    if(L<=r&&mid1,r);
    return ans%mod;
}

void Cinit()
{
    bool nmk;int phii;
    for(int j,i=0;i<=cnt;i++)
    {
        phii=phi[i];
        j=KSM(c,16384,phii,nmk);
        num[i][0][0]=num[i][0][1]=1;
        mark[i][0][0]=mark[i][0][1]=0;
        for(int p=1;p<16384;p++)
        {
            num[i][p][0]=1LL*num[i][p-1][0]*c%phii;
            num[i][p][1]=1LL*num[i][p-1][1]*j%phii;
            mark[i][p][0]=mark[i][p-1][0]|(1LL*num[i][p-1][0]*c>=phii);
            mark[i][p][1]=mark[i][p-1][1]|nmk|(1LL*num[i][p-1][1]*j>=phii);
        }
    }
}

int main()
{
//  freopen("verbinden.in","r",stdin);
//  freopen("verbinden.out","w",stdout);
    n=input();int m=input();mod=phi[0]=input();c=input();
    while((phi[++cnt]=Gphi(phi[cnt-1]))!=1);
    phi[++cnt]=1;
    Cinit();
    for(int i=1;i<=n;i++)A[i]=input();
    BUILD(1,1,n);
    int cz;
    for(int i=1;i<=m;i++)
    {
        cz=input();L=input();R=input();
        if(cz)printf("%d\n",sum(1,1,n));
        else CH(1,1,n);
    }
 }

你可能感兴趣的:(数论)