题解 P3747 【[六省联考2017]相逢是问候】

诗乃

2019-10-10 16:23:12

Solution

前置芝士扩展欧拉定理: ![](https://cdn.luogu.com.cn/upload/image_hosting/kxeno956.png) 引理:一个数至多取$O(log)$次欧拉函数会变为1. 口胡证明:对于偶数,所有小于它的偶数都与它不互质,其欧拉函数至少为其$\frac{1}{2}$。对于奇数,其欧拉函数必是偶数。证毕。 由此可得某数在修改$O(log)$次之后将不再改变。 考虑暴力分块。 将序列分为$\frac{n}{B}$个大小为$B$的块。修改时暴力修改整块与散块,若某一次修改后发现当前块没有数字被改变,说明继续修改这个块也不再生效,打上标记不再修改。 考虑如何修改单点。 记录每个位置的修改次数,用扩展欧拉定理直接嵌套求解,容易写出像这样的代码: ```cpp void getans(int x, int t, int p) { //x为当前位置初始值,t为修改次数,p为当前模数。 if(!t) return x%p; if(p==1) return 0; int b = getans(x, t-1, phi[p])+phi[p]; return pwc(b, p); //其中pwc(b, p)表示求c^b mod p的值。 } ``` 这样写显然是有一点点小问题的,代码中并没有判断$b$与$\varphi(p)$的大小,引入一个结构体来存储数据是否已经超过$p$即可解决此问题。 ```cpp struct Node {int x, flag;}; Node getans(int x, int t, int P) { if(!t) return (Node) {x%P, x>=P}; if(P == 1) return (Node) {0, 1}; Node tmp = getans(x, t-1, phi[P]); int b = tmp.x + phi[P] * tmp.flag; return pwc(b, P); //在pwc()中也需要判断,详见代码。 } ``` 这样看起来单点修改的时间是$O(log^2n)$的,难以接受。考虑优化快速幂。 考虑暴力分块。设最大指数为$V$,则可以对于每一个模数预处理出以下两个信息: 1、$c^1,c^2,c^3,c^4,c^5....,c^{\sqrt{V}}$。 2、$c^{\sqrt{V}},c^{2\sqrt{V}},c^{3\sqrt{V}},c^{4\sqrt{V}},....c^{V}$。 对于每一个指数$b<V$,有: $$b=k\sqrt{V}+t(k \in N,t≤\sqrt{V})$$ 由于模数最多只有$O(logn\sqrt{V})$预处理$O(1)$求解$c$的幂次方。 由此达成了$O(logn)$单点修改。 随便假设一下$log(n),log(p)$同阶来分析复杂度。(懒) 由于每个块最多被整块修改$O(logn)$次,每次修改复杂度为$O(Blogn)$,共有$O(\frac{n}{B})$个块,因此整块修改的复杂度为均摊$O(nlog^2n)$,散块修改复杂度$O(nBlogn)$。 对于询问操作,复杂度为$O(nB+\frac{n^2}{B})$ 当$B=\sqrt{\frac{n}{log_2n}}$的时候复杂度最优,为$O(nlog^2n+(nlogn)^{\frac{3}{2}})$ 可以草过本题。上代码: ```cpp #include <bits/stdc++.h> #define bl(x) ((x-1)/siz+1) #define L(x) (x-1)*siz+1 #define R(x) std::min(x*siz, n) using namespace std; const int MAXN = 50050, MAXB = 250, GSM = 12000, MAXV = 1e8; int s[MAXB], a[MAXN], tag[MAXN], n, m, p, c, siz, tms[MAXN], ori[MAXN], notp[MAXV+5], phi[MAXV+5], prime[MAXV+5]; int id[MAXV+5], _c[150][GSM+5], __c[150][GSM+5], ccc, cntp, _b[150][GSM+5], __b[150][GSM+5]; struct Node {int x, flag;}; void read(int &x) { char ch; while(ch = getchar(), ch < '!'); x = ch - 48; while(ch = getchar(), ch > '!') x = (x << 3) + (x << 1) + ch - 48; } void write(int x) {if(x > 9) write(x / 10); putchar(x % 10 + 48);} int Getphi(int x) { int ans = x; for(int i = 2; i * i <= x; ++i) if(x % i == 0) { ans = ans / i * (i-1); while(x % i == 0) x /= i; } if(x > 1) ans = ans / x * (x-1); return ans; } void init(int P) { id[P] = ++ccc; _c[id[P]][0] = __c[id[P]][0] = 1; for(int i = 1; i <= GSM; ++i) { _c[id[P]][i] = 1ll*_c[id[P]][i-1]*c%P; _b[id[P]][i] = (_b[id[P]][i-1] || _c[id[P]][i-1]*c >= P); } __c[id[P]][1] = _c[id[P]][GSM]; __b[id[P]][1] = _b[id[P]][GSM]; for(int i = 2; i <= GSM; ++i) { __c[id[P]][i] = 1ll*__c[id[P]][i-1]*__c[id[P]][1]%P; __b[id[P]][i] = (__b[id[P]][i-1] || __c[id[P]][i-1]*_c[id[P]][1] >= P); } } Node pwc(int b, int P) { return (Node) {1ll*__c[id[P]][b/GSM]*_c[id[P]][b%GSM]%P, __b[id[P]][b/GSM]||_b[id[P]][b%GSM]||__c[id[P]][b/GSM]*_c[id[P]][b%GSM]>=P}; } Node getans(int x, int t, int P) { if(!t) return (Node) {x%P, x>=P}; if(P == 1) return (Node) {0, 1}; Node tmp = getans(x, t-1, phi[P]); int b = tmp.x + phi[P] * tmp.flag; return pwc(b, P); } void update(int b) {for(int i = L(b); i <= R(b); ++i) s[b] = (s[b] + a[i]) % p;} void modify(int i) { s[bl(i)] -= a[i]; ++tms[i]; a[i] = getans(ori[i], tms[i], p).x; s[bl(i)] += a[i]; s[bl(i)] = (s[bl(i)] + p) % p; } void work(int b) { if(tag[b]) return; tag[b] = 1; for(int i = L(b); i <= R(b); ++i) { int t = a[i]; modify(i); if(t != a[i]) tag[b] = 0; } } int main() { read(n); read(m); read(p); read(c); siz = max(1.0, sqrt(n/log2(n))); int tmp = p; for(; tmp > 1; tmp = phi[tmp]) phi[tmp] = Getphi(tmp), init(tmp); init(1); for(int i = 1; i <= n; ++i) read(a[i]), ori[i] = a[i]; for(int i = 1; i <= bl(n); ++i) update(i); for(int opt, l, r; m--; ) { read(opt); read(l); read(r); if(!opt) { if(bl(l) == bl(r)) for(int i = l; i <= r; ++i) modify(i); else { for(int i = l; i <= R(bl(l)); ++i) modify(i); for(int i = bl(l)+1; i <= bl(r)-1; ++i) work(i); for(int i = L(bl(r)); i <= r; ++i) modify(i); } } else { if(bl(l) == bl(r)) { int res = 0; for(int i = l; i <= r; ++i) res = (res + a[i]) % p; write(res); putchar('\n'); } else { int res = 0; for(int i = l; i <= R(bl(l)); ++i) res = (res + a[i]) % p; for(int i = bl(l)+1; i <= bl(r)-1; ++i) res = (res + s[i]) % p; for(int i = L(bl(r)); i <= r; ++i) res = (res + a[i]) % p; write(res); putchar('\n'); } } } } ```