bzoj4869 [Shoi2017]相逢是问候
可以发现, $$\displaystyle c{c{c^\cdots}}$$ 从下到上对应的模数是 \(p,\varphi(p),\varphi(\varphi(p)),\varphi(\varphi(\varphi(p))),\ldots ,1,1\)。(为什么有两个 \(1\)?一会儿再说)
因此当一个数被修改了很多次以后它就不会再变了,维护一下一个数被修改了几次。
再来说为什么两个 \(1\)。以 \(n=1,p=3,a_1=0,c=2\) 为例:
\[2^{2^0} \bmod 3=2,2^{2^{2^0}} \bmod 3=1
\]
这是由于扩展欧拉定理得到的。也就是说,
\[2^{2^{0 \bmod 1} \bmod 2} \bmod 3=2,
\]
\[2^{2^{2^{0 \bmod 1}\bmod 1 + 1}\bmod 2}\bmod 3=1.
\]
这就造成了在 \(\bmod 1\) 时的不同。如果只有一个 \(1\),会造成上下等价。其实,这是一组非常特殊的数据。
懒得卡常了,以下代码仅能 A bzoj。
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
int n, m, p, c, phi[105], cnt, a[50005], opt, uu, vv;
bool isy;
struct SGT{
int sum[200005], tag[200005];
void build(int o, int l, int r){
if(l==r) sum[o] = a[l];
else{
int mid=(l+r)>>1;
int lson=o<<1;
int rson=lson|1;
if(l<=mid) build(lson, l, mid);
if(mid<r) build(rson, mid+1, r);
sum[o] = (sum[lson] + sum[rson]) % p;
}
}
int ksm(int a, int b, int p){
int re=1;
while(b){
if(b&1){
if((ll)re*a>=p) isy = true;
re = ((ll)re * a) % p;
}
if(b!=1 && (ll)a*a>=p) isy = true;
a = ((ll)a * a) % p;
b >>= 1;
}
return re;
}
int changeOneEle(int x, int hmn){
int tmp=x;
if(tmp>phi[hmn]) tmp = tmp%phi[hmn] + phi[hmn];
for(int i=hmn-1; i>=0; i--){
isy = false;
tmp = ksm(c, tmp, phi[i]);
if(isy) tmp += phi[i];
}
return tmp%p;
}
void update(int o, int l, int r, int x, int y){
if(tag[o]>=cnt-1) return ;
if(l==r)
sum[o] = changeOneEle(a[l], ++tag[o]);
else{
int mid=(l+r)>>1;
int lson=o<<1;
int rson=lson|1;
if(x<=mid) update(lson, l, mid, x, y);
if(mid<y) update(rson, mid+1, r, x, y);
sum[o] = (sum[lson] + sum[rson]) % p;
tag[o] = min(tag[lson], tag[rson]);
}
}
int query(int o, int l, int r, int x, int y){
if(l>=x && r<=y) return sum[o];
else{
int mid=(l+r)>>1;
int lson=o<<1;
int rson=lson|1;
int re=0;
if(x<=mid) re = (re + query(lson, l, mid, x, y)) % p;
if(mid<y) re = (re + query(rson, mid+1, r, x, y)) % p;
return re;
}
}
}sgt;
int getPhi(int x){
int re=x;
for(int i=2; i*i<=x; i++)
if(x%i==0){
re = re / i * (i - 1);
while(x%i==0) x /= i;
}
if(x>1) re = re / x * (x - 1);
return re;
}
void prePhi(){
phi[cnt++] = p;
while(phi[cnt-1]!=1){
phi[cnt] = getPhi(phi[cnt-1]);
cnt++;
}
phi[cnt++] = 1;
}
int main(){
cin>>n>>m>>p>>c;
for(int i=1; i<=n; i++)
scanf("%d", &a[i]);
sgt.build(1, 1, n);
prePhi();
while(m--){
scanf("%d %d %d", &opt, &uu, &vv);
if(opt==0) sgt.update(1, 1, n, uu, vv);
else printf("%d\n", sgt.query(1, 1, n, uu, vv));
}
return 0;
}
我们发现快速幂挺费时间的,那就把它搞掉。参考
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
int n, m, p, c, phi[105], cnt, a[50005], opt, uu, vv, ccc[75][10005], ttt[75][10005];
bool isy, fcc[75][10005], ftt[75][10005];
struct SGT{
int sum[200005], tag[200005];
void build(int o, int l, int r){
if(l==r) sum[o] = a[l];
else{
int mid=(l+r)>>1;
int lson=o<<1;
int rson=lson|1;
if(l<=mid) build(lson, l, mid);
if(mid<r) build(rson, mid+1, r);
sum[o] = (sum[lson] + sum[rson]) % p;
}
}
int ksm(int b, int x){
ll tmp=(ll)ttt[x][b/10000]*ccc[x][b%10000];
if(tmp>=phi[x])tmp = tmp % phi[x] + phi[x];
return tmp;
}
int changeOneEle(int x, int hmn){
int tmp=x;
if(tmp>phi[hmn]) tmp = tmp%phi[hmn] + phi[hmn];
for(int i=hmn-1; i>=0; i--){
isy = false;
tmp = ksm(tmp, i);
}
return tmp%p;
}
void update(int o, int l, int r, int x, int y){
if(tag[o]>=cnt-1) return ;
if(l==r)
sum[o] = changeOneEle(a[l], ++tag[o]);
else{
int mid=(l+r)>>1;
int lson=o<<1;
int rson=lson|1;
if(x<=mid) update(lson, l, mid, x, y);
if(mid<y) update(rson, mid+1, r, x, y);
sum[o] = (sum[lson] + sum[rson]) % p;
tag[o] = min(tag[lson], tag[rson]);
}
}
int query(int o, int l, int r, int x, int y){
if(l>=x && r<=y) return sum[o];
else{
int mid=(l+r)>>1;
int lson=o<<1;
int rson=lson|1;
int re=0;
if(x<=mid) re = (re + query(lson, l, mid, x, y)) % p;
if(mid<y) re = (re + query(rson, mid+1, r, x, y)) % p;
return re;
}
}
}sgt;
int getPhi(int x){
int re=x;
for(int i=2; i*i<=x; i++)
if(x%i==0){
re = re / i * (i - 1);
while(x%i==0) x /= i;
}
if(x>1) re = re / x * (x - 1);
return re;
}
void prePhi(){
phi[cnt++] = p;
while(phi[cnt-1]!=1){
phi[cnt] = getPhi(phi[cnt-1]);
cnt++;
}
phi[cnt++] = 1;
}
void preKsm(int x){
ccc[x][0] = 1>=phi[x]?1%phi[x]+phi[x]:1;
for(int i=1; i<=10000; i++){
ccc[x][i] = ((ll)ccc[x][i-1] * c)>=phi[x]?((ll)ccc[x][i-1] * c)%phi[x]+phi[x]:((ll)ccc[x][i-1] * c);
}
ttt[x][0] = 1>=phi[x]?1%phi[x]+phi[x]:1;
int tmp=ccc[x][10000]>=phi[x]?ccc[x][10000]%phi[x]+phi[x]:ccc[x][10000];
for(int i=1; i<=10000; i++)
ttt[x][i] = ((ll)ttt[x][i-1] * tmp)>=phi[x]?((ll)ttt[x][i-1] * tmp)%phi[x]+phi[x]:((ll)ttt[x][i-1] * tmp);
}
int main(){
cin>>n>>m>>p>>c;
for(int i=1; i<=n; i++)
scanf("%d", &a[i]);
sgt.build(1, 1, n);
prePhi();
for(int i=0; i<cnt; i++)
preKsm(i);
while(m--){
scanf("%d %d %d", &opt, &uu, &vv);
if(opt==0) sgt.update(1, 1, n, uu, vv);
else printf("%d\n", sgt.query(1, 1, n, uu, vv));
}
return 0;
}
拙いものと思えども、
その手に握る其れこそが、
いつか幻想を生んでいく。