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

好题啊!

调了一个中午,发现有一条语句 \(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;
}

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

时间: 2024-08-30 12:26:13

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

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

4869: [Shoi2017]相逢是问候 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1313  Solved: 471[Submit][Status][Discuss] Description Informatikverbindetdichundmich. 信息将你我连结.B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以 分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每

[六省联考2017]相逢是问候

相逢是问候 2017-09-09 Description Informatikverbindetdichundmich. 信息将你我连结.B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为这个结果可

洛谷P3747 [六省联考2017]相逢是问候

传送门 题解 扩展欧拉定理. 线段树维护,已经全改到底了的节点就不管,不然暴力修改下去. //Achen #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<queue> #include<cmath> const int N=5

bzoj千题计划271:bzoj4869: [六省联考2017]相逢是问候

http://www.lydsy.com/JudgeOnline/problem.php?id=4869 欧拉降幂+线段树,每个数最多降log次,模数就会降为1 #include<cmath> #include<cstdio> #include<iostream> using namespace std; #define N 50001 int n,m,p,c; int a[N]; int sum[N<<2]; int tag[N<<2]; in

P3747 [六省联考2017]相逢是问候

题意 如果对一个数操作\(k\)次,那么这个数会变成\(c^{c^{...^{a_i}}}\),其中\(c\)有\(k\)个. 根据P4139 上帝与集合的正确用法这道题,我们可以知道一个数不断变为自己的欧拉函数,大约\(log\)次就会变成1,而任何数模\(1\)都是\(0\),于是我们可以用势能线段树解决. 因为模数不变,因此我们可以预处理所有\(\varphi(\varphi(...\varphi(p)...))\),之后在线段树上记录操作次数. 这样是三个\(log\)的,因为还要快速幂

【bzoj4869】[Shoi2017]相逢是问候 线段树+扩展欧拉定理

Description Informatikverbindetdichundmich. 信息将你我连结.B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以 分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是 输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为 这个结果可能会很大,所以你只需要输出结

P3746 [六省联考2017]组合数问题

P3746 [六省联考2017]组合数问题 \(dp_{i,j}\)表示前\(i\)个物品,取的物品模\(k\)等于\(r\),则\(dp_{i,j}=dp_{i-1,(j-1+k)%k}+dp_{i-1,j}\) \(dp_{i,0},dp_{i,1},dp_{i,2}.....dp_{i,k-1}\) \(\Longrightarrow\) \(dp_{i+1,0},dp_{i+1,1},dp_{i+1,2}.....dp_{i+1,k-1}\) 仔细想想,你能构造出矩阵的 #include

[luogu] P3745 [六省联考2017]期末考试 (贪心)

P3745 [六省联考2017]期末考试 题目描述 有 \(n\) 位同学,每位同学都参加了全部的 \(m\) 门课程的期末考试,都在焦急的等待成绩的公布. 第 \(i\) 位同学希望在第 \(t_i\)? 天或之前得知所有课程的成绩.如果在第 \(t_i\) 天,有至少一门课程的成绩没有公布,他就会等待最后公布成绩的课程公布成绩,每等待一天就会产生 \(C\) 不愉快度. 对于第 \(i\) 门课程,按照原本的计划,会在第 \(b_i\)? 天公布成绩. 有如下两种操作可以调整公布成绩的时间:

六省联考2017

期末考试 sol 因为时间范围很小,所以可以利用单调性求出对于每一个时间$t$,当最晚的成绩公布时间为$t$时学生产生的不满意度总和$f_t$和让所有课程的公布时间不大于$t$的前提下课程产生的最小不满意度$g_t$.复杂度$O(nlogn)$,瓶颈是排序. 但是上面那个做法太不优雅了.我们可以发现$g_t$和$f_t$差分之后的数组都是单调不减,也就是$f_t+g_t$差分之后单调不减,也就意味着$f_t+g_t$这个数列是单谷数列.我们在时间范围上三分数列极小值即可. 然后因为三分太慢获得了