这题是六省联考的...据说数据还出了点锅,心疼六省选手QAQ

  首先要知道扩展欧拉定理...

  可以发现每次区间操作都会使模数进行一次phi操作,而一个数最多取logp次phi就会变成1,这时后面的指数就没有用了,以后这个数的答案就不会变化了,也就是说一个数最多只会进行log次修改,那么我们就可以用线段树维护,如果某棵子数的最小操作次数达到了使模数变成1的次数我们就不需要修改了。

  但是我们发现快速幂还有一个log,如果不优化的话三个log很有可能TLE。这个时候就有新操作了,底数是一定的c,指数最大为1e9,那么我们可以预处理出c^1~c^10000,设t为c^10000,再预处理出t^1~t^10000,这样对于每个询问我们只需要拆成前后两部分分别在c和t的表里找到并乘起来就好了,这样之后一个点最多被修改logn次,线段树效率O(NlogN),总复杂度O(Nlog^2N)。

  要注意的点(数据出锅的地方)是预处理的时候计算使模数变成1的最小操作次数也就是几次幂运算之后答案不变,必须预处理到phi(1)=1,不能预处理到phi(2)=1,因为如果序列中有0的话,它是<phi(2)的,这时候指数加上phi(2)可能会出错。所以需要递归到phi(1)=1的地方,这样即使指数是0,加一之后c^0和c^1都一定>=phi(2)。

  有一些大爷的博客就给出了只递归到phi(2)=1的反例,如 链接

  因为对c进行不同次的幂操作的模数并不同,不能递推,所以要预处理的东西还有c的logp次幂操作,这个可以直接递归计算,因为递归层数不会超过logp,枚举序列中的数和模数为O(NlogN),快速幂已经预处理了,所以总的复杂度为O(NlogNP)。至于递归的时候如何判断指数是否大于phi(当前模数),因为2进行4次幂操作之后已经非常大了(远大于p),所以只需要判断接下来的递归次数是否大于5就好了(如果小于5还要判断最顶部那个序列里的数和c进行(递归层数-1)次幂操作的数乘起来是否大于phi(当前模数))。

  还要预处理的就是p进行logp次操作途中的所有phi值,然后这题就完了

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define MOD(x) ((x)>=mod?(x)-mod:(x))
#define ll long long
using namespace std;
const int maxn=, inf=1e9;
struct poi{int sum, cnt;}tree[maxn<<];
int n, m, c, mod, cnt, ty, x, y;
int a[maxn], p[maxn], mi1[][maxn], mi2[][maxn], cmi[][maxn];
inline void read(int &k)
{
int f=; k=; char c=getchar();
while(c<'' || c>'') c=='-'&&(f=-), c=getchar();
while(c<='' && c>='') k=k*+c-'', c=getchar();
k*=f;
}
inline int min(int a, int b){return a<b?a:b;}
inline void pushup(int x)
{
tree[x].sum=tree[x<<].sum+tree[x<<|].sum;
tree[x].sum=MOD(tree[x].sum);
tree[x].cnt=min(tree[x<<].cnt, tree[x<<|].cnt);
}
void build(int x, int l, int r)
{
if(l==r){read(a[l]); tree[x].sum=a[l]; return;}
int mid=(l+r)>>;
build(x<<, l, mid); build(x<<|, mid+, r);
pushup(x);
}
inline int phi(int n)
{
int ans=n;
for(int i=;i*i<=n;i++)
if(!(n%i))
{
ans=ans/i*(i-);
while(!(n%i)) n/=i;
}
if(n>) ans=ans/n*(n-);
return ans;
}
inline int power(int n, int x){return 1ll*mi2[x][n/]*mi1[x][n%]%p[x];}
inline int getmi(int x, int y, int mod)
{
if(c==)return ; if(!y) return x%p[mod];
int nxt=min(y, x+); ll now=(nxt==y?x:c);
if(now>=p[mod+]) return power(getmi(x, y-, mod+)+p[mod+], mod);
for(int i=nxt-;i>=;i--)
{
ll t=now, now=;
for(int j=;j<=t;j++)
{
now*=c;
if(now>=p[mod+]) return power(getmi(x, y-, mod+)+p[mod+], mod);
}
}
return power(getmi(x, y-, mod+), mod);
}
void prepare()
{
p[]=mod; while(p[cnt]-) p[++cnt]=phi(p[cnt-]); p[++cnt]=;
for(int i=;i<=cnt;i++)
{
mi1[i][]=; for(int j=;j<=;j++) mi1[i][j]=1ll*mi1[i][j-]*c%p[i];
mi2[i][]=; for(int j=;j<=;j++) mi2[i][j]=1ll*mi2[i][j-]*mi1[i][]%p[i];
}
for(int i=;i<=n;i++)
for(int j=;j<=cnt;j++)
if(a[i]) cmi[j][i]=getmi(a[i], j, );
else cmi[j][i]=getmi(, j-, );
}
void update(int x, int l, int r, int cl, int cr)
{
if(tree[x].cnt>=cnt) return;
if(l==r){tree[x].cnt++, tree[x].sum=cmi[tree[x].cnt][l]; return;}
int mid=(l+r)>>;
if(cl<=mid) update(x<<, l, mid, cl, cr);
if(cr>mid) update(x<<|, mid+, r, cl, cr);
pushup(x);
}
inline int query(int x, int l, int r, int cl, int cr)
{
if(cl<=l && r<=cr) return tree[x].sum;
int mid=(l+r)>>, ret=;
if(cl<=mid) ret=query(x<<, l, mid, cl, cr);
if(cr>mid) ret+=query(x<<|, mid+, r, cl, cr), ret=MOD(ret);
return ret;
}
int main()
{
read(n); read(m); read(mod); read(c);
build(, , n); prepare();
for(int i=;i<=m;i++)
{
read(ty); read(x); read(y);
if(!ty) update(, , n, x, y);
else printf("%d\n", query(, , n, x, y));
}
}

bzoj4869: [Shoi2017]相逢是问候(欧拉函数+线段树)的更多相关文章

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

    题目链接 BZOJ4869 题解 这题调得我怀疑人生,,结果就是因为某些地方\(sb\)地忘了取模 前置题目:BZOJ3884 扩展欧拉定理: \[c^a \equiv c^{a \mod \varp ...

  2. LightOJ 1370 Bi-shoe and Phi-shoe 欧拉函数+线段树

    分析:对于每个数,找到欧拉函数值大于它的,且标号最小的,预处理欧拉函数,然后按值建线段树就可以了 #include <iostream> #include <stdio.h> ...

  3. loj1370(欧拉函数+线段树)

    传送门:Bi-shoe and Phi-shoe 题意:给出多个n(1<=n<=1e6),求满足phi(x)>=n的最小的x之和. 分析:先预处理出1~1e6的欧拉函数,然后建立一颗 ...

  4. [BZOJ4026]dC Loves Number Theory 欧拉函数+线段树

    链接 题意:给定长度为 \(n\) 的序列 A,每次求区间 \([l,r]\) 的乘积的欧拉函数 题解 考虑离线怎么搞,将询问按右端点排序,然后按顺序扫这个序列 对于每个 \(A_i\) ,枚举它的质 ...

  5. LOJ #2142. 「SHOI2017」相逢是问候(欧拉函数 + 线段树)

    题意 给出一个长度为 \(n\) 的序列 \(\{a_i\}\) 以及一个数 \(p\) ,现在有 \(m\) 次操作,每次操作将 \([l, r]\) 区间内的 \(a_i\) 变成 \(c^{a_ ...

  6. [LNOI] 相逢是问候 || 扩展欧拉函数+线段树

    原题为2017六省联考的D1T3 给出一个序列,m次操作,模数p和参数c 操作分为两种: 1.将[l,r]区间内的每个数x变为\(c^x\) 2.求[l,r]区间内数的和%p 首先,我们要了解一些数论 ...

  7. SHOI 2017 相逢是问候(扩展欧拉定理+线段树)

    题意 https://loj.ac/problem/2142 思路 一个数如果要作为指数,那么它不能直接对模数取模,这是常识: 诸如 \(c^{c^{c^{c..}}}\) 的函数递增飞快,不是高精度 ...

  8. BZOJ 4034: [HAOI2015]树上操作 [欧拉序列 线段树]

    题意: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 显然树链剖分可做 ...

  9. BZOJ 4034 树上操作(树的欧拉序列+线段树)

    刷个清新的数据结构题爽一爽? 题意: 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x ...

随机推荐

  1. jQuery-mobilevalidate使用 的一些心得,小小总结

    在做M站时比较纠结的是表单验证,不像pc端,移动端的验证要求插件更小更轻量,更加灵活,说不定是冒气泡的报错提示?! 介绍一款好用的移动端的表单验证插件:jQuery-mobilevalidate: 代 ...

  2. DAY06、元组、字典、集合

    一.元组 1.定义:参数为for可以循环的对象 t1 = (1, 2)     t2 = tuple((1, 2))     t3 = (1, )        #定义一个只有一个值的元组 2.常用操 ...

  3. 网站滚动n个像素后,头部固定

    //固顶 $(window).scroll(function() { var top = $(window).scrollTop(); if(top>=1200){ $(".x_men ...

  4. $.ajax的async设置true和false的区别一点笔记

    async的默认值是true 当async为true时,为异步请求 如果一个$.ajax的函数在另一个函数中调用,不一定会等该函数调用完再加载完函数 导致产生空值的问题 而在JS函数中调用$.ajax ...

  5. Spring Boot 构建电商基础秒杀项目 (十二) 总结 (完结)

    SpringBoot构建电商基础秒杀项目 学习笔记 系统架构 存在问题 如何发现容量问题 如何使得系统水平扩展 查询效率低下 活动开始前页面被疯狂刷新 库存行锁问题 下单操作步骤多,缓慢 浪涌流量如何 ...

  6. 学习 Spring (十六) AOP API

    Spring入门篇 学习笔记 Spring AOP API 是 Spring 1.2 历史用法,现在仍然支持 这是 Spring AOP 基础,现在的用法也是基于历史的,只是更简便了 Pointcut ...

  7. pip 指定版本

    要用 pip 安装特定版本的 Python 包,只需通过 == 操作符 指定,例如: pip install -v pycrypto==2.3 将安装 pycrypto 2.3 版本.

  8. hdu-3294(最长回文子串)

    题意:给你一个字符和一个字符串让你求出最长回文子串并且输出来,答案需要根据给出的字符转换一下,就是将给出的字符认定为a,然后依次向后推: 解题思路:manacher模板+一些处理 代码: #inclu ...

  9. centos6 nginx 安装【转】

    原文 https://www.cnblogs.com/yaoximing/p/6068622.html 1.下载nginx 方法一 wget http://nginx.org/download/ngi ...

  10. BZOJ3438小M的作物——最小割

    题目描述 小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子 有1个(就是可以种一棵作物)(用1...n编号),现在,第i种作物种植在A中种植可 ...