【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因为

这个结果可能会很大,所以你只需要输出结果mod p的值即可。

Input

第一行有三个整数n,m,p,c,所有整数含义见问题描述。

接下来一行n个整数,表示a数组的初始值。

接下来m行,每行三个整数,其中第一个整数表示了操作的类型。

如果是0的话,表示这是一个修改操作,操作的参数为l,r。

如果是1的话,表示这是一个询问操作,操作的参数为l,r。

1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p

Output

对于每个询问操作,输出一行,包括一个整数表示答案mod p的值。

Sample Input

4 4 7 2
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3

Sample Output

0
3

Sol

根据扩展欧拉定理,参照bzoj3884,我们发现某个数字做一定次数比操作之后就不会变了,这个次数在\(logn\)左右,所以就可以用线段树维护区间和以及这个区间的数字有没有都处理完毕,然后直接维护即可。时间复杂度\(nlog^3n\),由于本题数据范围\(50000\),所以可以通过。

注意细节:判断某个数字有没有超过\(\varphi(p)\),以及\(\varphi(1)=\varphi(2)=1\),但是我们必须要在\(p=1\)的时候才能停止。

Code

#include <bits/stdc++.h>
using namespace std;
int n,m,P,c,K,op,l,r,a[50005],p[50005],V[50005],pr[50005],tot,sm[200005],mn[200005];
int phi(int x)
{
    int res=x;
    for(int i=1;pr[i]*pr[i]<=x;i++)
    {
        if(x%pr[i]) continue;
        res-=res/pr[i];
        while(x%pr[i]==0) x/=pr[i];
    }
    if(x>1) res-=res/x;return res;
}
void build(int x,int l,int r)
{
    if(l==r){scanf("%d",&a[l]);sm[x]=a[l]%P;mn[x]=0;return;}
    int M=(l+r)>>1;build(x<<1,l,M);build(x<<1|1,M+1,r);
    sm[x]=(sm[x<<1]+sm[x<<1|1])%P;mn[x]=min(mn[x<<1],mn[x<<1|1]);
}
int ksm(int a,int b,int P,bool &f)
{
    int res=1;bool gg=0;
    for(;b;b>>=1,a=1ll*a*a%P)
    {
        if(b&1) f|=(gg|(1ll*res*a>=P)),res=1ll*res*a%P;
        if(1ll*a*a>=P) gg=1;
    }
    return res;
}
int cal(int dep,int x)
{
    int res=x;if(res>=p[dep]) res=res%p[dep]+p[dep];
    while(dep)
    {
        dep--;bool flag=0;
        res=ksm(c,res,p[dep],flag);
        if(flag) res+=p[dep];
    }
    return res%p[dep];
}
void upd(int x,int l,int r,int b,int e)
{
    if(mn[x]>=K) return;
    if(l==r){mn[x]++;sm[x]=cal(mn[x],a[l]);return;}
    int M=(l+r)>>1;
    if(b<=M) upd(x*2,l,M,b,e);if(e>M) upd(x*2+1,M+1,r,b,e);
    sm[x]=(sm[x<<1]+sm[x<<1|1])%P;mn[x]=min(mn[x<<1],mn[x<<1|1]);
}
int que(int x,int l,int r,int b,int e)
{
    if(b<=l&&r<=e) return sm[x];
    int M=(l+r)>>1;
    return ((b<=M?que(x*2,l,M,b,e):0)+(e>M?que(x*2+1,M+1,r,b,e):0))%P;
}
int main()
{
    for(int i=2;i<=50000;i++)
    {
        if(!V[i]) pr[++tot]=i;
        for(int j=1;j<=tot&&i*pr[j]<=50000;j++){V[i*pr[j]]=1;if(i%pr[j]==0) break;}
    }
    scanf("%d%d%d%d",&n,&m,&P,&c);
    p[0]=P;while(p[K]!=1){++K;p[K]=phi(p[K-1]);}p[++K]=1;
    for(build(1,1,n);m--;)
    {
        scanf("%d%d%d",&op,&l,&r);
        if(op==0) upd(1,1,n,l,r);
        else printf("%d\n",que(1,1,n,l,r));
    }
}

原文地址:https://www.cnblogs.com/CK6100LGEV2/p/9419411.html

时间: 2024-07-30 15:17:03

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

[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)中的每

【BZOJ4869】相逢是问候 [线段树]

相逢是问候 Time Limit: 40 Sec  Memory Limit: 512 MB[Submit][Status][Discuss] Description Informatikverbindetdichundmich. 信息将你我连结.B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以 分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是 输入的一个常数,也就是执行赋值

bzoj4869 [Shoi2017]相逢是问候

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4869 [题解] 发现好像没有办法普通维护. 给小盆友们江数论的时候江过x^m mod p = x^(m mod phi(p)) mod p 发现一个数进行phi操作最多log次. 暴力就行啦qwq 注意就是phi的那个最后要补一个phi[++pn] = 1. 为什么呢?因为phi(1) = 1(展开到最后,还要多展开一层(!)) 那么就行啦! 复杂度不大会分析qwq 照理性分析可能是log

【BZOJ4869】相逢是问候(线段树,欧拉定理)

[BZOJ4869]相逢是问候(线段树,欧拉定理) 题面 BZOJ 题解 根据欧拉定理递归计算(类似上帝与集合的正确用法) 所以我们可以用线段树维护区间最少的被更新的多少次 如果超过了\(\varphi\)的限制 就不用再计算了 如果需要计算就每次暴力算 这样的复杂度\(O(nlog^2)\) #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<

[六省联考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)\)

bzoj 4869: [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因为 这个结果可能会很大,所以你只需要输出结

[Shoi2017]相逢是问候

传送门 这种“暴力线段树”可以考虑一下是不是有什么特殊性质. 对于这道题什么要知道: 可能这里写得比较清楚(摘自) 感觉就是一个迭代的过程. 而一个数在操作k次之后就可以不用再操作了. 然后使用欧拉定理的时候要特判一下: x>=phi时,最后要加一个phi 小于则不加. 预处理出所有的phi,记得最后一个phi为1. #include<bits/stdc++.h> #define LL long long #define N 50003 using namespace std; int

小学扩展欧拉定理

学了一下扩展欧拉定理,不会证,记了个结论,笔记的话,随便去网上搜一搜吧.-bzoj3884:上帝与集合的正确用法无脑板子题额 #include <cstdio> #include <cstring> #include <algorithm> typedef long long LL; inline int Pow(int x,int y,int P){ int ret=1; while(y){ if(y&1)ret=(LL)ret*x%P; x=(LL)x*x%

POJ1195 Mobile phones 【二维线段树】

Mobile phones Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 14291   Accepted: 6644 Description Suppose that the fourth generation mobile phone base stations in the Tampere area operate as follows. The area is divided into squares. The