洛谷 P3747 [六省联考2017]相逢是问候 线段树+扩展欧拉定理

题目:
https://www.luogu.org/problemnew/show/P3747

分析:
幂次可以考虑扩展欧拉定理。
对于一个模数 p p p,使得 ϕ ( ϕ ( . . . ϕ ( p ) ) ) = 1 \phi(\phi(...\phi(p)))=1 ϕ(ϕ(...ϕ(p)))=1,最少 ϕ \phi ϕ的个数 l i m lim lim
每次的 c c c是一样的,显然当一个位置修改次数大于 l i m lim lim。这个位置就不变了。
维护一棵线段树,每个节点维护这个区间是否所有的点都到达了 l i m lim lim和区间和。修改直接暴力到叶子节点,对于每一个叶子结点跑扩展欧拉。幂次要预处理。

代码:

// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include 
#include 
#include 
#define LL long long

const int maxn=5e4+7;

using namespace std;

int n,m,mod,c,op,x,y,lim;
int a[maxn],phi[101];

struct node{
    int l,r,data,sum;
}t[maxn*8];

int getphi(int p)
{
    int tmp=1;
    for (int i=2;i<=trunc(sqrt(p));i++)
    {
        if (p%i==0)
        {
            tmp*=i-1;
            p/=i;
            while (p%i==0) tmp*=i,p/=i;
        }
    }
    if (p>1) tmp*=p-1;
    return tmp;
}

void build(int p,int l,int r)
{
    if (l==r)
    {
        t[p].sum=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;
}

LL power(LL x,LL y,LL p)
{
    LL ret=1;
    int flag=0,flag1=0;
    while (y)
    {
        if (y&1)
        {
            ret*=x;
            flag=flag1;
            if (ret>=p) ret%=p,flag=1;
        }
        if (x>=p) flag1=1,x%=p;
        x=x*x;
        if (x>=p) flag1=1,x%=p;
        y>>=1;
    }
    if (flag) return ret+p;
    return ret;
}

LL calc(int k,int x,int d)
{
    if (!k)
    {
        if (x>=phi[d]) return x%phi[d]+phi[d];
                  else return x;
    }
    if (phi[d]==1) return 1;
    LL y=calc(k-1,x,d+1);
    return power(c,y,phi[d]);
}

void ins(int p,int l,int r,int x,int y)
{
    if (t[p].data>lim) return;
    if (l==r)
    {
        t[p].data++;
        t[p].sum=(LL)calc(t[p].data,a[l],0)%mod;
        return;
    }
    int mid=(l+r)/2;
    if (y<=mid) ins(p*2,l,mid,x,y);
    else if (x>mid) ins(p*2+1,mid+1,r,x,y);
    else
    {
        ins(p*2,l,mid,x,mid);
        ins(p*2+1,mid+1,r,mid+1,y);
    }
    t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;
    t[p].data=min(t[p*2].data,t[p*2+1].data);
}

int query(int p,int l,int r,int x,int y)
{
    if ((l==x) && (r==y)) return t[p].sum;
    int mid=(l+r)/2;
    if (y<=mid) return query(p*2,l,mid,x,y);
    else if (x>mid) return query(p*2+1,mid+1,r,x,y);
    else return (query(p*2,l,mid,x,mid)+query(p*2+1,mid+1,r,mid+1,y))%mod;
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&mod,&c);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    build(1,1,n);  
    phi[0]=mod;
    while (phi[lim]!=1)
    {
        lim++;
        phi[lim]=getphi(phi[lim-1]);
    }
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&op,&x,&y);
        if (op==0) ins(1,1,n,x,y);
              else printf("%d\n",query(1,1,n,x,y));
    }
}

你可能感兴趣的:(线段树,数论)