【BZOJ4105】平方运算(THUSC2015)-线段树+找规律

i=lrXi
做法:本题需要用到线段树+找规律。
看到这种特别诡异的区间操作,不能直接用线段树维护,那肯定又要分析操作的性质了。注意到因为有一个模数p都满足两个性质:
1.所有数操作至多不超过11步就会进入一个循环。
2.所有循环的循环节长度的LCM不超过60
于是对于操作区间中每一个没有进入循环的数,我们就暴力算出它的值,从它更新到根,这一个部分的总时间复杂度是)。那么我们就完成了这一题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,loop[10010]={0},looplen[400010]={0},now[400010]={0},st[10010];
int len[10010]={0},vis[10010]={0},pos[10010]={0},tag[400010]={0};
ll p,a[100010],loopsum[400010][62],seg[400010]={0};

int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}

int lcm(int a,int b)
{
    return a*b/gcd(a,b);
}

void pushdown(int no)
{
    if (tag[no])
    {
        tag[no<<1]+=tag[no];
        tag[no<<1|1]+=tag[no];
        seg[no<<1]=(seg[no<<1]+tag[no])%looplen[no<<1];
        seg[no<<1|1]=(seg[no<<1|1]+tag[no])%looplen[no<<1|1];
        tag[no]=0;
    }
}

void pushup(int no)
{
    seg[no]=0;
    if (!looplen[no<<1]) seg[no]+=seg[no<<1];
    else seg[no]+=loopsum[no<<1][seg[no<<1]];
    if (!looplen[no<<1|1]) seg[no]+=seg[no<<1|1];
    else seg[no]+=loopsum[no<<1|1][seg[no<<1|1]];
    if (looplen[no<<1]&&looplen[no<<1|1])
    {
        ll nowl=seg[no<<1],nowr=seg[no<<1|1];
        if (!looplen[no]) looplen[no]=lcm(looplen[no<<1],looplen[no<<1|1]);
        seg[no]=0;
        for(int i=0;i<looplen[no];i++)
        {
            loopsum[no][i]=loopsum[no<<1][nowl]+loopsum[no<<1|1][nowr];
            nowl=(nowl+1)%looplen[no<<1];
            nowr=(nowr+1)%looplen[no<<1|1];
        }
    }
}

void check(int no)
{
    if (!len[seg[no]])
    {
        looplen[no]=loop[seg[no]];
        for(int i=0;i<looplen[no];i++)
        {
            loopsum[no][i]=seg[no];
            seg[no]=seg[no]*seg[no]%p;
        }
        seg[no]=0;
    }
}

void buildtree(int no,int l,int r)
{
    if (l==r)
    {
        seg[no]=a[l];
        check(no);
        return;
    }
    int mid=(l+r)>>1;
    buildtree(no<<1,l,mid);
    buildtree(no<<1|1,mid+1,r);
    pushup(no);
}

void modify(int no,int l,int r,int s,int t)
{
    if (l>=s&&r<=t&&looplen[no])
    {
        tag[no]++;
        seg[no]=(seg[no]+1)%looplen[no];
        return;
    }
    if (l==r)
    {
        seg[no]=seg[no]*seg[no]%p;
        check(no);
        return;
    }
    int mid=(l+r)>>1;
    pushdown(no);
    if (s<=mid) modify(no<<1,l,mid,s,t);
    if (t>mid) modify(no<<1|1,mid+1,r,s,t);
    pushup(no);
}

ll query(int no,int l,int r,int s,int t)
{
    if (l>=s&&r<=t)
    {
        if (!looplen[no]) return seg[no];
        else return loopsum[no][seg[no]];
    }
    int mid=(l+r)>>1;
    ll sum=0;
    pushdown(no);
    if (s<=mid) sum+=query(no<<1,l,mid,s,t);
    if (t>mid) sum+=query(no<<1|1,mid+1,r,s,t);
    return sum;
}

int main()
{
    scanf("%d%d%lld",&n,&m,&p);
    for(ll i=0;i<p;i++)
        if (!vis[i])
        {
            int x=i,top=0;
            while(!vis[x])
            {
                st[++top]=x;
                pos[x]=top;
                vis[x]=i+1;
                x=x*x%p;
            }
            if (vis[x]!=i+1)
            {
                for(int j=1;j<=top;j++)
                    len[st[j]]=top-j+1+len[x];
            }
            else
            {
                for(int j=1;j<pos[x];j++)
                    len[st[j]]=pos[x]-j;
                for(int j=pos[x];j<=top;j++)
                    loop[st[j]]=top-pos[x]+1;
            }
        }

    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    buildtree(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int op,l,r;
        scanf("%d%d%d",&op,&l,&r);
        if (!op) modify(1,1,n,l,r);
        else printf("%lld
",query(1,1,n,l,r));
    }

    return 0;
}