牛客练习赛22 E 简单数据结构1(拓展欧拉定理+树状数组)

题目

解析:

首先了解欧拉定理1     欧拉定理2

再是欧拉线性筛

线性筛

最后是拓展欧拉定理

还有小的知识是树状数组的区间更新+单点查询 链接

上官方题解

先线性筛phi

然后考虑用拓展欧拉定理降幂

(这里a的指数部分应该是a^{x mod \phi (p) +(x\geq \phi (p) ?\phi (p): 0)} mod p)

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

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

首先我们发现后面的phi[p]这一项是可能不会加的

这个怎么办呢?

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

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

然后加点特判即可

总复杂度O( p + mlognlog*(v) )

 

这里需要注意的地方有两点

1.a^{x mod \phi (p) +(x\geq \phi (p) ?\phi (p): 0)} mod p上面的x必须是完整的值。例如题目中a[l]^(a[l+1]^(a[l+2]^(a[l+3]^(......a[r])中计算a[l+1]是否需要加上\phi (p)时,是依据(a[l+1]^(a[l+2]^(a[l+3]^(......a[r])>\phi (p)?来决定的,不是a[l+1]>\phi (p)?来决定的

2.在dfs返回指数k时,计算a[i]^k必须要先让a[i]%p..........这个具体的我也不知道为什么,反正没有就只能过60%的样例

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 2e7+10;
const int MAX = 5e5+100;
int prime[MAXN],mark[MAXN];
int tot,phi[MAXN];
ll btree[MAX];

int n;
ll a[MAX];
void getphi(int N)
{
    phi[1]=1;
    tot=0;
    for(int i=2;i<=N;i++)
    {
        if(!mark[i])
        {
            prime[++tot]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=tot;j++)
        {
            if(i*prime[j]>N) break;
            mark[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else
            {
                //phi[i*prime[j]]=phi[i]*phi[prime[j]];
				phi[i*prime[j]]=phi[i]*(prime[j]-1);
            }
        }
    }
}

inline int lowbit(int x)
{
    return x&(-x);
}

void add(int l,ll x)  //区间更新
{
    for(int i=l;i<=n;i+=lowbit(i))
    {
        btree[i]+=x;
    }
}

ll query(int l)   //单点查询
{
    ll sum=0;
    for(int i=l;i>0;i-=lowbit(i))
        sum+=btree[i];
    return sum;
}


ll quick(ll a,ll b,ll p,int& ok)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
        {
            if(ans*a>=p) ok=1;
            ans=ans*a%p;

        }
        if(a*a>=p) ok=1;
        a=a*a%p;
        b=b>>1;
    }
    return ans;
}

ll  dfs(int now,int r,ll p)
{
    ll tmp=query(now);
    if(now==r||p==1)
    {
        //if(cnt!=0)
            return tmp%p+(tmp>=p?p:0);
        /*else
            return tmp%p;*/
    }
    /*if(p==1)
    {
        //ll tmp=query(now);
        //if(cnt!=0)
            return 1;
        //else
            //return 0;
    }*/
    ll k=dfs(now+1,r,(ll)phi[p]);
    int ok=0;
    if(tmp>=p) ok=1,tmp=tmp%p;   //!!!tmp=tmp%p
    ll ans= quick(tmp,k,p,ok);
    if(ok)
    {
        ans+=p;
    }
    //if(cnt!=0)
        return ans;
    //else
    //    return ans%p;

}

int main()
{
    getphi(MAXN-2);
    int m;
    scanf("%d%d",&n,&m);
    a[0]=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        //add(i,a[i]);
        //add(i+1,-a[i]);
        add(i,a[i]-a[i-1]);
    }
    for(int i=0;i<m;i++)
    {
        int l,r,mode;
        ll x;
        scanf("%d%d%d%lld",&mode,&l,&r,&x);
        if(mode==1)
        {
            add(l,x);
            add(r+1,-x);
        }
        else
        {
            printf("%lld\n",dfs(l,r,x)%x);  //%x用于去除dfs内a[l]%x时加上x的情况
        }
    }
    return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值