bzoj 4869: [Shoi2017]相逢是问候 数论+线段树

来源:互联网 发布:淘宝怎么优化宝贝排名靠前 编辑:程序博客网 时间:2024/05/16 19:42

题意

给出一个序列a,模数p和一个常数c,要求资瓷两个操作:
0 l r表示把l<=i<=r的a[i]变为ca[i]
1 l r表示查询a[l..r]的和。
n,m<=50000,a[i],c,p<=10^9

分析

首先这题要用到欧拉定理EXT:x>=φ(p)cxcxmodφ(p)+φ(p)
因为注意到式子右边的次方上面又是一个相同形式的式子,我们可以用同样的方式递归处理。
那么我们就可以预处理出phi[i]表示p取i次phi后的值。因为一个数最多取O(logn)次phi后就会变为1,所以i最大为logp.然后最后还要多加一个1.
还有一个结论就是一个数在做常数次修改操作后就会变为不动点。
所以我们可以每次找到未被修改完的区间,然后每次暴力修改。
这样复杂度大概是O(nlog3n),在bzoj上能过。

这里还可以优化掉快速幂的那一个log,就是把一个数分成大于216216,然后分别预处理出来,就可以O(1)做快速幂了。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int N=50005;const int M=35;int n,m,phi[M],c,p1,a[N],table[32][1<<16][2];struct tree{int s,tms;}t[N*5];bool flag[32][1<<16][2];int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}int ksm(int x,int y,int MOD,bool &flag){    int ans=1;flag=0;    while (y)    {        if (y&1) flag|=((LL)ans*x>=MOD),ans=(LL)ans*x%MOD;        flag|=((LL)x*x>=MOD&&y>1);x=(LL)x*x%MOD;y>>=1;    }    return ans;}int bksm(int x,int y,bool &w){    int p=x&((1<<16)-1),q=x>>16;    w=flag[y][p][0]|flag[y][q][1]|((LL)table[y][p][0]*table[y][q][1]>=phi[y]);    return (LL)table[y][p][0]*table[y][q][1]%phi[y];}int calc(int x,int p){    int ret=x;bool flag;    if (ret>=phi[p]) ret=ret%phi[p]+phi[p];    while (p--)    {        x=ret;        ret=bksm(x,p,flag);        if (flag) ret+=phi[p];    }    return ret%phi[0];}int get_phi(int n){    int w=n,tmp=n;    for (int i=2;i*i<=n;i++)        if (tmp%i==0)        {            w=w/i*(i-1);            while (tmp%i==0) tmp/=i;        }    if (tmp>1) w=w/tmp*(tmp-1);    return w;}void updata(int d){    t[d].s=(t[d*2].s+t[d*2+1].s)%phi[0];    t[d].tms=min(t[d*2].tms,t[d*2+1].tms);}void build(int d,int l,int r){    if (l==r)    {        t[d].s=a[l]%phi[0];        return;    }    int mid=(l+r)/2;    build(d*2,l,mid);build(d*2+1,mid+1,r);    updata(d);}void modify(int d,int l,int r,int x,int y){    if (x>y||t[d].tms==p1) return;    if (l==r)    {        t[d].tms++;        t[d].s=calc(a[l],t[d].tms);        return;    }    int mid=(l+r)/2;    modify(d*2,l,mid,x,min(y,mid));    modify(d*2+1,mid+1,r,max(x,mid+1),y);    updata(d);}int query(int d,int l,int r,int x,int y){    if (x>y) return 0;    if (l==x&&r==y) return t[d].s;    int mid=(l+r)/2;    return (query(d*2,l,mid,x,min(y,mid))+query(d*2+1,mid+1,r,max(x,mid+1),y))%phi[0];}void prework(){    for (int i=0;i<=p1;i++)    {        table[i][0][0]=table[i][0][1]=1;        if (phi[i]==1) flag[i][0][0]=flag[i][0][1]=1;        int tmp=ksm(c,1<<16,phi[i],flag[i][1][1]),c1=ksm(c,1,phi[i],flag[i][1][0]);        table[i][1][0]=c1;table[i][1][1]=tmp;        for (int j=2;j<(1<<16);j++)        {            flag[i][j][0]=flag[i][j-1][0]|((LL)table[i][j-1][0]*c>=phi[i]);            table[i][j][0]=(LL)table[i][j-1][0]*c%phi[i];            flag[i][j][1]=flag[i][j-1][1]|((LL)table[i][j-1][1]*tmp>=phi[i]);            table[i][j][1]=(LL)table[i][j-1][1]*tmp%phi[i];        }    }}int main(){    n=read();m=read();phi[0]=read();c=read();    p1=0;    while (phi[p1]>1) p1++,phi[p1]=get_phi(phi[p1-1]);    phi[++p1]=1;    prework();    for (int i=1;i<=n;i++) a[i]=read();    build(1,1,n);    while (m--)    {        int op=read(),l=read(),r=read();        if (!op) modify(1,1,n,l,r);        else printf("%d\n",query(1,1,n,l,r));    }    return 0;}
阅读全文
0 0