zoukankan      html  css  js  c++  java
  • 【BZOJ4105】平方运算(THUSC2015)-线段树+找规律

    测试地址:平方运算
    题目大意:维护一个序列X,再给定一个模数p,要求支持两个操作:1.将一个区间内的Xi修改为Xi2modp。2.求i=lrXi
    做法:本题需要用到线段树+找规律。
    看到这种特别诡异的区间操作,不能直接用线段树维护,那肯定又要分析操作的性质了。注意到因为有一个模数p,所以平方很多次之后一定会进入一个循环,根据打表发现数据中所有的p都满足两个性质:
    1.所有数操作至多不超过11步就会进入一个循环。
    2.所有循环的循环节长度的LCM不超过60
    于是对于操作区间中每一个没有进入循环的数,我们就暴力算出它的值,从它更新到根,这一个部分的总时间复杂度是O(11nlogn)。而对于一个区间,如果该区间内所有数都已经进入循环了,那么整个区间数字和也有循环,长度就是每个数所在循环的长度的LCM,前面我们已经得出这个数不超过60了。那么我们每次在向上更新时,应该计算出该区间节点在循环节内每个位置的和,这个可以根据它的左右子区间的信息合并,每更新一个节点的复杂度是O(60)。而在之后如果再更新到这整个区间,就可以O(1)修改每一个涉及到的整区间的信息了,因此这个部分的时间复杂度为O(60nlogn)。那么我们就完成了这一题。
    以下是本人代码:

    #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;
    }
  • 相关阅读:
    SaltStack api使用
    saltstack批量管理文件和计划任务
    Kubernetes应用管理器OpenKruise之CloneSet
    Kubernetes日志系统新贵Loki-Stack
    Prometheus Operator自定义监控项
    MySQL错误修复:Table xx is marked as crashed and last (automatic?) repair failed
    kubernetes存储类与PV与PVC关系及实践
    手把手教你使用rpm部署ceph集群
    什么是dockerfile?
    RabbitMQ
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793359.html
Copyright © 2011-2022 走看看