牛客练习赛22-E:简单数据结构1(拓展欧拉定理降幂)

链接:https://www.nowcoder.com/acm/contest/132/E

时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

给一个长为n的序列,m次操作,每次操作:
1.区间 加
2.对于区间 ,查询   ,一直到 -
请注意每次的模数不同。

输入描述:

第一行两个整数 n,m 表示序列长度和操作数

接下来一行,n个整数,表示这个序列

接下来m行,可能是以下两种操作之一:

操作1:区间[l,r]加上 x

操作2:查询区间[l,r]的那个式子mod p的值

输出描述:

对于每个询问,输出一个数表示答案
示例1

输入

复制
6 4
1 2 3 4 5 6
2 1 2 10000007
2 2 3 5
1 1 4 1
2 2 4 10

输出

复制
1
3
1

备注:

n , m <= 500000

序列中每个数在 [1,2e9] 内,x <= 2e9 , p <= 2e7

ax mod p=ax mod ϕ(p)+(x>ϕ(p)?ϕ(p):0)moP

我们发现对一个数取欧拉函数,log次就会变成1,而任何数模1肯定=0,所以就可以算出来了。

然而这么做还会有一些小问题。

首先我们发现后面的phi[p]这一项是可能不会加的。这个怎么办呢?

因为这个不断地幂次增长速度极快,很少几项就能够增长到远大于模数的地步。

那就先暴力的处理前面几项,然后再正常做,这样就减少了讨论次数。

但是这么搞还会存在一个问题,就是如果前几个常数次暴力处理的全是1,增长就是假的。

不过考虑到有1的话后面的全都被变成了1,所以完全没有必要担心这个,找到1就从1开始做好了。

#include
using namespace std;
const int MAX=1e6+10;
const double PI=acos(-1.0);
typedef long long ll;
int p[20000001];
ll A[500005];
inline int read()
{
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
inline void phi()  //欧拉函数
{
    memset(p,0,sizeof p);
    p[1]=1;
    for(int i=2;i<=2e7;i++)
    {
        if(p[i])continue;
        for(int j=i;j<=2e7;j+=i)
        {
            if(p[j]==0)p[j]=j;
            p[j]=p[j]/i*(i-1);
        }
    }
}
inline void add(int n,int x,int y)
{
    while(x<=n)
    {
        A[x]+=y;
        x+=x&(-x);
    }
}
inline ll ask(int x)
{
    ll ans=0;
    while(x)
    {
        ans+=A[x];
        x-=x&(-x);
    }
    return ans;
}
inline int POW(ll x,ll n,int mod)
{
    int res=1;
    x%=mod;
    while(n)
    {
        if(n&1)res=res*x%mod;
        x=x*x%mod;
        n>>=1;
    }
    return res;
}
inline ll cal(int x,int y,int mod)
{
    if(ask(x)%mod==0)return 0;
    if(mod==1)return 1;
    if(x==y)return ask(x)>=mod?ask(x)%mod+mod:ask(x);
    ll last,pre;
    for(int i=x+1;i<=min(y,x+5);i++)
    {
        if(ask(i)==1||i==min(y,x+5))
        {
            last=ask(i);
            for(int j=i-1;j>x;j--)
            {
                pre=last;
                last=1;
                while(pre--)
                {
                    last*=ask(j);
                    if(last>=p[mod])return POW(ask(x),cal(x+1,y,p[mod])+p[mod],mod);
                }
            }
            break;
        }
    }
    return POW(ask(x),last,mod);
}
int main()
{
    phi();
    int n=read();
    int m=read();
    for(int i=1;i<=n;i++)
    {
        ll x=read();
        add(n,i,x);
        add(n,i+1,-x);
    }
    while(m--)
    {
        int op=read();
        int x=read();
        int y=read();
        int mod=read();
        if(op==1)
        {
            add(n,x,mod);
            add(n,y+1,-mod);
        }
        else printf("%lld\n",cal(x,y,mod)%mod);
    }
    return 0;
}

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