bzoj4869&&jzoj5214[Shoi2017]相逢是问候 线段树+欧拉定理
来源:互联网 发布:freemind软件 编辑:程序博客网 时间:2024/05/17 03:04
真是码农啊,当然可能是我比较菜= =码死我了。。
考场(训练)上觉得自己两题稳了所以只打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(const T &a, const T &b) {return a < b ? a : b;}template <class T>T Max(const T &a, const T &b) {return a > b ? a : b;}typedef long long ll;const int SN = 100000 + 10;int k, cnt, phi[SN];ll sum[SN<<2], time[SN<<2], c, p, a[SN];int table[32][1<<16][2];void Read(ll &x) { ll in = 0, f = 1; char ch = getchar(); while(ch<'0' || ch>'9') {if(ch=='-') f = -1; ch = getchar();} while(ch>='0' && ch<='9') {in = in*10+ch-'0'; ch = getchar();} x = in*f;}int get_phi(int n){ int res = n, a = n; for(int i = 2;i * i <= a;i++){ if(a%i==0){ res=res/i*(i-1); while(a%i==0) a/=i; } } if(a>1) res=res/a*(a-1); return res; } int pow(int a,int b,int MOD){ int res = 1; while(b) { if(b & 1) res = (ll)res * a % MOD; a = (ll)a * a % MOD; b >>= 1; } return res;}void pre() { phi[0] = p; while(phi[k] != 1) {++k;phi[k] = get_phi(phi[k-1]);} phi[++k] = 1; fo(i,0,k) { int r=pow(c,1<<16,phi[i]); table[i][0][0]=table[i][0][1]=1; for(int j=1;j<(1<<16);j++) { table[i][j][0]=1ll*table[i][j-1][0]*c%phi[i]; table[i][j][1]=1ll*table[i][j-1][1]*r%phi[i]; } }}inline ll Power(int a,int b){ return 1ll*table[b][a&65535][0]*table[b][a>>16][1]%phi[b];}ll Calc(int x, int t) { int res=x; if (res>=phi[t])res=res%phi[t]+phi[t]; while (t--) { x=res; res=Power(x,t); if(t&&!res)res+=phi[t]; } return res%p;}void pushup(int rt) { sum[rt] = (sum[rt<<1] + sum[rt<<1|1])%p; time[rt] = Min(time[rt<<1], time[rt<<1|1]);}void build(int l, int r, int rt) { if(l == r) { Read(sum[rt]), a[l] = sum[rt]; a[l] %= p, sum[rt] %= p; return ; } int mid = (l+r)>>1; build(l, mid, rt<<1), build(mid+1, r, rt<<1|1); pushup(rt);}void Modify(int QL, int QR, int l, int r, int rt) { if(time[rt] >= k) return ; if(l == r) { time[rt]++, sum[rt] = Calc(a[l], time[rt]); return ; } int mid = (l+r)>>1; if(QL <= mid) Modify(QL, QR, l, mid, rt<<1); if(QR > mid) Modify(QL, QR, mid+1, r, rt<<1|1); pushup(rt);}ll Query(int QL, int QR, int l, int r, int rt) { if(QL <= l && QR >= r) return sum[rt]%p; int mid = (l+r)>>1; ll ans = 0; if(QL <= mid) ans = (ans + Query(QL, QR, l, mid, rt<<1))%p; if(QR > mid) ans = (ans + Query(QL, QR, mid+1, r, rt<<1|1))%p; return ans;}int main() { ll n, m, opt, x, y; Read(n), Read(m), Read(p), Read(c); pre(); build(1, n, 1); for(int i = 1; i <= m; i++) { Read(opt), Read(x), Read(y); if (!opt) Modify(x, y, 1, n ,1); else printf("%lld\n",Query(x,y,1,n,1)%p); } return 0;}
- bzoj4869&&jzoj5214[Shoi2017]相逢是问候 线段树+欧拉定理
- [BZOJ4869][Shoi2017]相逢是问候 扩展欧拉定理+势能线段树
- 【BZOJ4869】相逢是问候(线段树+欧拉定理)
- 【BZOJ4869】【SHOI2017】相逢是问候
- BZOJ 4869 [Shoi2017]相逢是问候 扩展欧拉定理+线段树
- [扩展欧拉定理] BZOJ 4869 [Shoi2017]相逢是问候
- [题解]bzoj4869 SHOI2017相逢是问候
- [省选] [扩展欧拉函数] [线段树] [BZOJ4869] [HLOI2017] 相逢是问候
- bzoj 4869: [Shoi2017]相逢是问候 数论+线段树
- 4869: [Shoi2017]相逢是问候
- [JZOJ5214]【HEOI、SXOI2017】相逢是问候(口胡)
- 【GDOI2018模拟7.9】相逢是问候
- 【GDOI2018模拟7.9】相逢是问候
- JLOI2017——相逢是问候,离别是祝愿
- 【BZOJ 4869】【2017六省联考】相逢是问候
- HDU 3074 Multiply game【线段树||zkw线段树||扩展gcd*乘法逆元||欧拉定理】
- 费马小定理&&欧拉定理
- 威尔逊定理,费马小定理,欧拉定理
- Python编码问题
- 通过摄像机视频设备或者流媒体服务器SDK获取到数据转换成RTMP流实现网页/手机微信播放
- The VirtualBox Linux kernel driver (vboxdrv) is either not loaded
- 踏入写博客的行列
- linux 权限管理命令
- bzoj4869&&jzoj5214[Shoi2017]相逢是问候 线段树+欧拉定理
- 剑指offer--从上往下打印二叉树--层次遍历、广度优先搜索
- Lombok 安装及入门
- JZOJ5213. 【GDOI2018模拟7.9】期末考试
- Ubuntu16.04 + cuda8.0 + GTX1080 + matlab14.04a + Opencv3.0 + caffe 安装教程
- Nvidia TX1安装 Ubuntu16.04LTS+ROS Kinetic版本+turtlebot 完整篇
- [2017湖南集训7-7]第一题 DP
- MD5信息摘要算法
- 爬虫系列之Requests库入门