码迷,mamicode.com
首页 > 其他好文 > 详细

[六省联考2017]相逢是问候(线段树+拓展欧拉定理)

时间:2019-01-06 11:59:23      阅读:157      评论:0      收藏:0      [点我收藏+]

标签:name   i++   window   reg   class   using   dfs   优化   for   

好题啊!

调了一个中午,发现有一条语句 \(RE\) 了。在 \(windows\) 下没关系,\(linux\) 下有问题,大大的问题。

while(phi[tot]!=1) phi[++tot]=calc_phi(phi[tot-1]);

算是拓展欧拉定理的题吧。线段树只是一个工具,最主要还是暴力修改。因为 \(\varphi\) 不断套下去最多会有 \(\lfloor \log n\rfloor\) 层,所以我们对于每一层暴力算一遍,加上快速幂,时间复杂度 \(O(n\log^3 n)\),显然可能被卡。怎么优化呢?

\(O(\log n)\) 的快速幂换成 \(O(1)\) 的快速幂就好了,时间复杂度 \(O(n\log^2 n)\)

\(Code\ Below:\)

#include <bits/stdc++.h>
#define int long long
#define lson (rt<<1)
#define rson (rt<<1|1)
using namespace std;
const int maxn=100000+10;
const int base=(1<<14)-1;
int n,m,p,c,a[maxn],sum[maxn<<2],Min[maxn<<2],pw1[55][maxn],pw2[55][maxn],phi[maxn],tot;
bool b1[55][maxn],b2[55][maxn],flag;

inline int read(){
    register int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return (f==1)?x:-x;
}

int calc_phi(int n){
    int ans=n,m=sqrt(n);
    for(int i=2;i<=m;i++){
        if(n%i==0){
            ans=ans/i*(i-1);
            while(n%i==0) n/=i;
        }
    }
    if(n>1) ans=ans/n*(n-1);
    return ans;
}

void pre(){
    int tmp=p;phi[0]=p;
    while(tmp!=1) tmp=calc_phi(tmp),phi[++tot]=tmp;
    phi[++tot]=1;
    for(int i=0;i<=tot;i++){
        pw1[i][0]=1;
        for(int j=1;j<=base+1;j++){
            pw1[i][j]=pw1[i][j-1]*c;
            if(pw1[i][j]>=phi[i]) b1[i][j]=1,pw1[i][j]%=phi[i];
            b1[i][j]|=b1[i][j-1];
        }
    }
    for(int i=0;i<=tot;i++){
        pw2[i][0]=1;
        b2[i][1]=b1[i][base+1];
        for(int j=1;j<=base;j++){
            pw2[i][j]=pw2[i][j-1]*pw1[i][base+1];
            if(pw2[i][j]>=phi[i]) b2[i][j]=1,pw2[i][j]%=phi[i];
            b2[i][j]|=b2[i][j-1];
        }
    }
}

int calc(int a,int dep){
    flag=0;
    int x=a&base,y=(a>>14)&base;
    int ans=pw1[dep][x]*pw2[dep][y];
    if(ans>=phi[dep]) flag=1,ans%=phi[dep];
    flag|=b1[dep][x]|b2[dep][y];
    return ans;
}

int dfs(int a,int dep,int lim){
    flag=0;
    if(dep==lim){
        if(a>=phi[dep]) flag=1,a%=phi[dep];
        return a;
    }
    int b=dfs(a,dep+1,lim);
    return calc(flag?b+phi[dep+1]:b,dep);
}

inline void pushup(int rt){
    sum[rt]=(sum[lson]+sum[rson])%p;
    Min[rt]=min(Min[lson],Min[rson]);
}

void build(int l,int r,int rt){
    if(l == r){
        sum[rt]=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}

void update(int L,int R,int l,int r,int rt){
    if(Min[rt]>=tot) return ;
    if(l == r){
        Min[rt]++;
        sum[rt]=dfs(a[l],0,Min[rt]);
        return ;
    }
    int mid=(l+r)>>1;
    if(L <= mid) update(L,R,l,mid,lson);
    if(R > mid) update(L,R,mid+1,r,rson);
    pushup(rt);
}

int query(int L,int R,int l,int r,int rt){
    if(L <= l && r <= R){
        return sum[rt];
    }
    int mid=(l+r)>>1,ans=0;
    if(L <= mid) ans=(ans+query(L,R,l,mid,lson))%p;
    if(R > mid) ans=(ans+query(L,R,mid+1,r,rson))%p;
    return ans;
}

signed main()
{
    n=read(),m=read(),p=read(),c=read();
    for(int i=1;i<=n;i++) a[i]=read();
    pre();build(1,n,1);
    int op,l,r;
    while(m--){
        op=read(),l=read(),r=read();
        if(op==0) update(l,r,1,n,1);
        if(op==1) printf("%lld\n",query(l,r,1,n,1));
    }
    return 0;
}

[六省联考2017]相逢是问候(线段树+拓展欧拉定理)

标签:name   i++   window   reg   class   using   dfs   优化   for   

原文地址:https://www.cnblogs.com/owencodeisking/p/10227494.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!