bzoj4869&&jzoj5214[Shoi2017]相逢是问候 线段树+欧拉定理

真是码农啊,当然可能是我比较菜= =码死我了。。
考场(训练)上觉得自己两题稳了所以只打30分,发现满分神TM难打。

题意:给你一个序列,m个操作,询问区间和%p,还有修改,每次修改v把v变成c^v.
给出n,m,p,c

没什么想法,看了题解发现是神题= =

首先我们需要几个结论:
扩展欧拉定理:
当x>=phi(p)时,c^x=c^(x%phi(p)+phi(p))mod p;
这个结论nb之处,在于p可以不是素数,甚至不用互质= =证明看po姐在bzoj3884写的题解
1.每个数不断的取phi,logp次以后再修改值不变。
这个很好证明,每次要不然奇数变偶数,要不然偶数取一半= =。
2.根据推论1,一个数被修改logp次以后再修改值不变。
证明略,比较复杂就不写了。
其他题解好像有些吧(懒癌患者晚期)

那么我们可以发现其实一个数假设被修改k次,那他其实相当于这样一个式子:
c^(c^(c^(c^(c^…(a[i]))))) ①
定义 p[i] 为 P 取过 i 次 phi 之后得到的数,即 p[0]
= P, p[i] = phi(p[i-1])

根据欧拉定理,我们可以发现,在计算上面那个式子的时候,由于c^x是迭代的,所以要%的数p也是迭代的,所以要把p[]求出来,这个自己推一下就好了。
然后具体做法如下:

先预处理出p,然后线段树修改的时候直接用快速幂暴力算,注意判断一下如果修改超过p数组的长度(再大p是0没有意义)就直接跳出,这样是均摊log的。
但是这样是log^3的,为什么呢。。
觉得是log^2的人都挺naive的

在这里我们默认了修改一次的时间是O(1),然而事实并非如此。对于一个数的快速幂,需要递归O(logP)层(因为每层指数的模数都是不一样的,都要重新算),所以一个数快速幂就要两个log,一共有nlogP个数(对不是n个数,x、c^x、c(c^x)……这些都要重新算的),所以快速幂的预处理就变成了三个log!虽然开了O2,但是如果常数爆炸的话很有可能被卡掉。 

所以我们要对快速幂进行离线处理,用分块算出前2^16和后面的。由于c最大1e9,我们我们把它看作1<<31,分成两半。
提前算出p以后枚举一下预处理就好了,p<=1e8,所以p数组的最大个数是26(logp),然后修改的时间复杂度就可以算作是常数级别的了。
代码巨丑(bzoj AC)

#include <cstdio>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
template <class T>T Min(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值