bzoj4105: [Thu Summer Camp 2015]平方运算【线段树】

Description

这里写图片描述

Input

第一行有三个整数N,M,p,分别代表序列的长度、平方操作与询问操作的总次数以及在平方操作中所要模的数。

接下来一行N个数代表一开始的序列{X1,X2,…,XN}。
接下来M行,每行三个整数op,l,r。其中op代表本次操作的类型。若op=0,代表这是一次平方操作,平方的区间为[l,r];如果op=1,代表这是一次询问操作,询问的区间为[l,r]。

Output

对于每次的询问操作,输出一行代表这段区间内数的总和。注意:答案没有对任何数取模。

Sample Input

3 3 11

1 2 3

1 1 3

0 1 3

1 1 3

Sample Output

6

14

HINT

对于100%的数据,∀i,Xi∈[0,p),l,r∈[1,n]

N,M,p的范围如下:

编号 N M p

1 1000 1000 233

2 1000 1000 2332

3 100000 100000 5

4 100000 100000 8192

5 100000 100000 23

6 100000 100000 45

7 100000 100000 37

8 55000 55000 4185

9 55000 55000 5850

10 55000 55000 2975

11 55000 55000 2542

12 55000 55000 2015

13 60000 60000 2003

14 65000 65000 2010

15 70000 70000 4593

16 75000 75000 4562

17 80000 80000 1034

18 85000 85000 5831

19 90000 90000 9905

20 100000 100000 9977

解题思路:

似乎没有什么技巧可以用来平方求和,但注意到模数较小,肯定会在这上面搞事情。
经过暴力打表可以得出,在(0,p)范围内的数,经过不超过11步的变换就可以进入一个循环节之中,并且所有整数的循环节长度的最小公倍数不会超过60。
于是就可以直接使用线段树来维护。在线段树的每一个节点上记下当前节点的数迭代0~循环节长度次以后的结果的和就可以了。
修改时类似区间开根的做法,每进入循环的直接修改到底。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
int getint()
{
    ll i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=100005,M=10000;
int n,m,p,a[N],nxt[M],vis[M],inc[M];
int sum[N<<2],len[N<<2],cir[N<<2][62],w[N<<2],in[N<<2],tag[N<<2];
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 init(int k,int v)
{
    sum[k]=v,in[k]=inc[v];
    if(in[k])
    {
        len[k]=1,cir[k][w[k]=0]=v;
        for(int i=nxt[v];i!=v;i=nxt[i])cir[k][len[k]++]=i;
    }
}
void update(int k)
{
    sum[k]=sum[k<<1]+sum[k<<1|1],in[k]=in[k<<1]&in[k<<1|1];
    if(in[k])
    {
        len[k]=lcm(len[k<<1],len[k<<1|1]),w[k]=0;
        int tx=w[k<<1],ty=w[k<<1|1];
        for(int i=0;i<len[k];i++)
        {
            cir[k][i]=cir[k<<1][tx++]+cir[k<<1|1][ty++];
            if(tx==len[k<<1])tx=0;if(ty==len[k<<1|1])ty=0;
        }
    }
}
void go(int k,int i)
{
    tag[k]+=i,w[k]+=i;
    while(w[k]>=len[k])w[k]%=len[k];
    sum[k]=cir[k][w[k]];
}
void pushdown(int k)
{
    go(k<<1,tag[k]),go(k<<1|1,tag[k]);
    tag[k]=0;
}
void build(int k,int l,int r)
{
    if(l==r){init(k,a[l]);return;}
    int mid=l+r>>1;
    build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    update(k);
}
void modify(int k,int l,int r,int x,int y)
{
    if(x<=l&&r<=y&&in[k]){go(k,1);return;}
    if(l==r){init(k,nxt[sum[k]]);return;}
    if(tag[k])pushdown(k);
    int mid=l+r>>1;
    if(x<=mid)modify(k<<1,l,mid,x,y);
    if(y>mid) modify(k<<1|1,mid+1,r,x,y);
    update(k);
}
int query(int k,int l,int r,int x,int y)
{
    if(x==l&&r==y)return sum[k];
    if(tag[k])pushdown(k);
    int mid=l+r>>1;
    if(y<=mid)return query(k<<1,l,mid,x,y);
    else if(x>mid)return query(k<<1|1,mid+1,r,x,y);
    else return query(k<<1,l,mid,x,mid)+query(k<<1|1,mid+1,r,mid+1,y);
}
int main()
{
    //freopen("lx.in","r",stdin);
    n=getint(),m=getint(),p=getint();
    for(int i=0;i<p;i++)inc[i]=1,nxt[i]=i*i%p;
    for(int i=0;i<p;i++)if(!vis[i])
    {
        int y=i;while(!vis[y])vis[y]=1,y=nxt[y];
        for(int x=i;x!=y;x=nxt[x])inc[x]=0;
    }
    for(int i=1;i<=n;i++)a[i]=getint();
    build(1,1,n);   
    while(m--)
    {
        int op=getint(),l=getint(),r=getint();
        if(op)printf("%d\n",query(1,1,n,l,r));
        else modify(1,1,n,l,r);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cdsszjj/article/details/80411690