bzoj4869 [Shoi2017]相逢是问候
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4869
【题解】
发现好像没有办法普通维护。
给小盆友们江数论的时候江过x^m mod p = x^(m mod phi(p)) mod p
upd: 这篇文章写的时候我也不知道我自己怎么想的了 好像上面这个有点问题。。但是还是AC了(逃
下面给出扩展欧拉定理的证明:https://zhuanlan.zhihu.com/p/24902174
以及,该定理的正确表示$x^m \equiv x^{m~mod~phi(p) + [m \geq phi(p)] phi(p)}(mod~p)$
注意当$m < phi(p)$的时候是不用加上$phi(p)$的。
发现一个数进行phi操作最多log次。
暴力就行啦qwq
注意就是phi的那个最后要补一个phi[++pn] = 1。
为什么呢?因为phi(1) = 1(展开到最后,还要多展开一层(!))
那么就行啦!
复杂度不大会分析qwq
照理性分析可能是log方到log三方之间的啦
反正跑的……挺快
upd: 两份代码均已经更正。 感谢Exbilar的提醒。
# include <stdio.h> # include <string.h> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 5e5 + 10; # define RG register # define ST static int n, m, p, c, a[M], phi[M], pn; inline int pwr(int a, int b, int p, bool &ok) { ok = 0; int ret = 1; while(b) { if(b&1) ok |= (1ll * ret * a >= p), ret = 1ll * ret * a % p; ok |= (1ll * a * a >= p && b != 1); a = 1ll * a * a % p; b >>= 1; } return ret; } inline int getphi(int n) { int ret = n; for (int i=2; i*i<=n; ++i) { if(n%i!=0) continue; ret = ret/i*(i-1); while(n%i==0) n/=i; } if(n!=1) ret=ret/n*(n-1); return ret; } inline int calc(int x, int p) { int ret = x; bool ok; if(ret >= phi[p]) ret = ret % phi[p] + phi[p]; while(p--) { x = ret; ret = pwr(c, x, phi[p], ok); if(ok) ret += phi[p]; } return ret % phi[0]; } namespace SMT { # define ls (x<<1) # define rs (x<<1|1) int v[M], tms[M]; inline void build(int x, int l, int r) { if(l==r) { v[x] = a[l] % phi[0]; tms[x] = 0; return ; } int mid = l+r>>1; build(ls, l, mid); build(rs, mid+1, r); v[x] = (v[ls]+v[rs])%phi[0]; tms[x] = min(tms[ls], tms[rs]); } inline void change(int x, int l, int r, int L, int R) { if(tms[x] >= pn) return; if(l == r) { tms[x] ++; v[x] = calc(a[l], tms[x]); return ; } int mid = l+r>>1; if(L <= mid) change(ls, l, mid, L, R); if(R > mid) change(rs, mid+1, r, L, R); v[x] = (v[ls]+v[rs])%phi[0]; tms[x] = min(tms[ls], tms[rs]); } inline int query(int x, int l, int r, int L, int R) { if(L <= l && r <= R) return v[x]; int mid = l+r>>1, re=0; if(L <= mid) re += query(ls, l, mid, L, R); if(R > mid) re += query(rs, mid+1, r, L, R); return re%phi[0]; } # undef ls # undef rs } int main() { scanf("%d%d%d%d", &n, &m, &p, &c); for (int i=1; i<=n; ++i) scanf("%d", a+i); phi[0] = p; while(p != 1) { int t = getphi(p); phi[++pn] = t; p = t; } phi[++pn] = 1; SMT::build(1, 1, n); int opt, l, r; while(m--) { scanf("%d%d%d", &opt, &l, &r); if(opt==0) SMT::change(1, 1, n, l, r); else printf("%d\n", SMT::query(1, 1, n, l, r)); } return 0; }
我们发现我们可以把快速幂的一个log强行压掉 这样就会快很多啦qwq
部分代码实现by zhouyi
# include <stdio.h> # include <string.h> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 5e5 + 10; # define RG register # define ST static int n, m, p, c, a[M], phi[M], pn; int pws[27][M][2]; bool pwk[27][M][2]; inline int pwr(int a, int b, int p, bool &ok) { ok = 0; int ret = 1; while(b) { if(b&1) ok |= (1ll * ret * a >= p), ret = 1ll * ret * a % p; ok |= (1ll * a * a >= p && b != 1); a = 1ll * a * a % p; b >>= 1; } return ret; } inline int getphi(int n) { int ret = n; for (int i=2; i*i<=n; ++i) { if(n%i!=0) continue; ret = ret/i*(i-1); while(n%i==0) n/=i; } if(n!=1) ret=ret/n*(n-1); return ret; } inline int pwrb(int b, int p, bool &ok){ p = min(p, pn); ok = 0; ok |= pwk[p][b & 16383][0]; ok |= pwk[p][b >> 14][1]; ok |= (1ll * pws[p][b & 16383][0] * pws[p][b >> 14][1] >= phi[p]); return 1ll * pws[p][b & 16383][0] * pws[p][b >> 14][1] % phi[p]; } inline int calc(int x, int p) { int ret = x; bool ok; if(ret >= phi[p]) ret = ret % phi[p] + phi[p]; while(p--) { x = ret; ret = pwrb(x, p, ok); if(ok) ret += phi[p]; } return ret % phi[0]; } namespace SMT { # define ls (x<<1) # define rs (x<<1|1) int v[M], tms[M]; inline void build(int x, int l, int r) { if(l==r) { v[x] = a[l] % phi[0]; tms[x] = 0; return ; } int mid = l+r>>1; build(ls, l, mid); build(rs, mid+1, r); v[x] = (v[ls]+v[rs])%phi[0]; tms[x] = min(tms[ls], tms[rs]); } inline void change(int x, int l, int r, int L, int R) { if(tms[x] >= pn) return; if(l == r) { tms[x] ++; v[x] = calc(a[l], tms[x]); return ; } int mid = l+r>>1; if(L <= mid) change(ls, l, mid, L, R); if(R > mid) change(rs, mid+1, r, L, R); v[x] = (v[ls]+v[rs])%phi[0]; tms[x] = min(tms[ls], tms[rs]); } inline int query(int x, int l, int r, int L, int R) { if(L <= l && r <= R) return v[x]; int mid = l+r>>1, re=0; if(L <= mid) re += query(ls, l, mid, L, R); if(R > mid) re += query(rs, mid+1, r, L, R); return re%phi[0]; } # undef ls # undef rs } int main() { scanf("%d%d%d%d", &n, &m, &p, &c); for (int i=1; i<=n; ++i) scanf("%d", a+i); phi[0] = p; while(p != 1) { int t = getphi(p); phi[++pn] = t; p = t; } phi[++pn] = 1; for(int i=0; i<=pn; ++i) { bool ok; int j = pwr(c, 16384, phi[i], ok); pws[i][0][0] = pws[i][0][1] = 1; pwk[i][0][0] = 0, pwk[i][0][1] = 0; for(int k=1; k<16384; k++) pwk[i][k][0] = pwk[i][k-1][0] | (1ll * pws[i][k-1][0] * c >= phi[i]), pws[i][k][0] = 1ll * pws[i][k-1][0] * c % phi[i], pwk[i][k][1] = pwk[i][k-1][1] | (ok || (1ll * pws[i][k-1][1] * j >= phi[i])), pws[i][k][1] = 1ll * pws[i][k-1][1] * j % phi[i]; } SMT::build(1, 1, n); int opt, l, r; while(m--) { scanf("%d%d%d", &opt, &l, &r); if(opt==0) SMT::change(1, 1, n, l, r); else printf("%d\n", SMT::query(1, 1, n, l, r)); } return 0; }