[BZOJ 4869][SXOI2017]相逢是问候(扩展欧拉定理+线段树)

Description

Informatik verbindet dich und mich.

信息将你我连结。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的值即可。

Solution

强迫症非要把这两道题放在一起…

学习了一下扩展欧拉定理

a^b≡x(mod m) 求x

gcd(a,m)=1,欧拉定理:a^b≡a^(b%φ(m))(mod m)

gcd(a,m)>1,且b>φ(m),扩展欧拉定理:a^b≡a^(b%φ(m)+φ(m))(mod m)

(b<=φ(m)时,直接用a^b求就可以了)

考试的时候数据出了一点问题啊…不过及时解决了也没造成太大影响

数据出错的原因据说是因为没有多展开phi[1]=1这一层?
cx≡cx%φ(p)+φ(p)(modp)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define MAXN 50005
using namespace std;
typedef long long LL;
int n,m,P,c,a[MAXN],phi[MAXN],k;
struct Node
{
    int l,r,cnt;
    LL sum;
}t[MAXN*4];
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c<‘0‘||c>‘9‘){
        if(c==‘-‘)f=-1;c=getchar();
    }
    while(c>=‘0‘&&c<=‘9‘){
        x=x*10+c-‘0‘;c=getchar();
    }
    return x*f;
}
inline int get_phi(int x)
{
    int res=x;
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            res=res/i*(i-1);
            while(x%i==0)x/=i;
        }
    }
    if(x!=1)res=res/x*(x-1);
    return res;
}
void build(int idx,int l,int r)
{
    t[idx].l=l,t[idx].r=r,t[idx].cnt=0;
    if(l==r){t[idx].sum=a[l]%phi[0];return;}
    int mid=(l+r)>>1;
    build(idx<<1,l,mid);
    build(idx<<1|1,mid+1,r);
    t[idx].sum=(t[idx<<1].sum+t[idx<<1|1].sum)%phi[0];
}
int query(int idx,int l,int r)
{
    if(l<=t[idx].l&&r>=t[idx].r)return t[idx].sum;
    int mid=(t[idx].l+t[idx].r)>>1;
    if(r<=mid)return query(idx<<1,l,r);
    else if(l>mid)return query(idx<<1|1,l,r);
    else return (query(idx<<1,l,r)+query(idx<<1|1,l,r))%phi[0];
}
inline LL pow(LL a,LL n,LL mod)
{
    LL res=1;
    while(n)
    {
        if(n&1)res=(res*a)%mod;
        a=(a*a)%mod;
        n>>=1;
    }
    return res%mod;
}
inline LL modify(int cnt,LL num)
{
    LL res=num;
    if(res>=phi[cnt])res=res%phi[cnt]+phi[cnt];
    for(int i=cnt;i>0;i--)
    {
        res=pow(c,res,phi[i-1]);
        if(!res)res+=phi[i-1];
    }
    return res%phi[0];
}
void change(int idx,int l,int r)
{
    if(t[idx].cnt>=k)return;
    if(t[idx].l==t[idx].r)
    {
        t[idx].cnt++;
        t[idx].sum=modify(t[idx].cnt,a[t[idx].l]);
        return;
    }
    int mid=(t[idx].l+t[idx].r)>>1;
    if(r<=mid)change(idx<<1,l,r);
    else if(l>mid)change(idx<<1|1,l,r);
    else change(idx<<1,l,r),change(idx<<1|1,l,r);
    t[idx].sum=(t[idx<<1].sum+t[idx<<1|1].sum)%phi[0];
    t[idx].cnt=min(t[idx<<1].cnt,t[idx<<1|1].cnt);
}
int main()
{
    n=read(),m=read(),P=read(),c=read();
    for(int i=1;i<=n;i++)a[i]=read();
    k=0;phi[0]=P;
    while(P!=1)
    {
        phi[++k]=get_phi(P);
        P=phi[k];
    }
    phi[++k]=1;
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int opt=read(),l=read(),r=read();
        if(!opt)change(1,l,r);
        else printf("%d\n",query(1,l,r));
    }
    return 0;
}
时间: 2024-07-30 15:16:46

[BZOJ 4869][SXOI2017]相逢是问候(扩展欧拉定理+线段树)的相关文章

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

[BZOJ 1018] [SHOI2008] 堵塞的交通traffic 【线段树维护联通性】

题目链接:BZOJ - 1018 题目分析 这道题就说明了刷题少,比赛就容易跪..SDOI Round1 Day2 T3 就是与这道题类似的..然而我并没有做过这道题.. 这道题是线段树维护联通性的经典模型. 我们线段树的一个节点表示一个区间的联通性,有 6 个 bool 值,表示这个区间的 4 个角上的点之间的联通性. 然后用两个子区间的联通性和两个子区间之间的连边情况合并出整个区间的联通性. 修改某条边时,先在边的数组中修改,然后从这条边所在的点的线段树叶子开始向上 Update . 询问两

bzoj 2733 永无乡 - 并查集 - 线段树

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输

[BZOJ 3218] A + B Problem 【可持久化线段树 + 网络流】

题目连接:BZOJ - 3218 题目分析 题目要求将 n 个点染成黑色或白色,那么我们可以转化为一个最小割模型. 我们规定一个点 i 最后属于 S 集表示染成黑色,属于 T 集表示染成白色,那么对于每个点 i 就要连边 (S, i, B[i]) 和 (i, T, W[i]). 这样,如果一个点属于 S 集,就要割掉与 T 相连的边,就相当于失去了染成白色的收益. 我们再来考虑 “奇怪的点”,一个点 i 变成奇怪的点的条件是:i 是黑色且存在一个白色点 j 满足 j < i && L

[BZOJ 4184] shallot 以时间为基底建线段树

题意 给定时长 $n$ , 每个时刻有某个元素出现或者消失, 求每个时刻所有元素的最大异或值. $n \le 500000$ . 分析 通过 map 或者 hash , 我们可以知道 $O(n)$ 个 "一个元素 $x$ 在 $[l, r]$ " 出现的信息. 对时间建立线段树, 每个节点开一个 vector , 对区间 $[l, r]$ 对应的所有节点插入一个 $x$ . 对线段树进行 DFS , 同时动态维护线性基. 实现 #include <cstdio> #incl

BZOJ 3022 [Balkan2012]The Best Teams(扫描线+线段树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3022 [题目大意] 给定n个球员,第i个球员年龄为AGEi,水平为SKILLi. 没有任何两个球员的水平相同.将这些球员按水平排序, 对于一次比赛,你需要选择若干个球员去比赛,但不能同时选择两个水平相邻的球员. m次询问,每次给定a和k,表示要在年龄不超过a的球员中选择不超过k个球员, 请计算skill和的最大值. [题解] 对于询问年龄的限制,我们可以通过扫描线来处理. 我们将所有

BZOJ 1112 [POI2008]砖块Klo(可持久化线段树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1112 [题目大意] 给出一个数列,对于一个操作,你可以对一个数+1,或者一个数-1, 问若使得数列中出现长度为m的连续相同的数,最少需要的操作数. [题解] 我们发现对于固定区间求最小操作,等价于求区间中数距离中位数差值和最小, 我们发现区间中位数可以利用主席树求区间kth来实现, 同时在主席树上维护权值线段树的区间真值和,那么对于每个区间中的数, 就能分别维护比中位数小的部分的和以

[BZOJ 1018][SHOI2008]堵塞的交通traffic(线段树)

Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可 以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有2C个 城市和3C-2条道路. 小人国的交通状况非常槽糕.有的时候由于交通堵塞,两座城市之间的道路会变得不连通, 直到拥堵解决,道路才会恢复畅通.初来咋到的你决心毛遂自荐到交通部某份差事,部长听说你来自一个科技高度 发达的世界,喜出望外地要求你编写一个查询应答系统,以挽救已经病入

bzoj 1798 [Ahoi2009]Seq 维护序列seq(线段树+传标)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1798 [题意] 给定一个序列,要求提供区间乘/加,以及区间求和的操作 [思路] 线段树+传标. 下传标记的方式可以类比这里 click here [代码] 1 #include<set> 2 #include<cmath> 3 #include<queue> 4 #include<vector> 5 #include<cstdio>