Loading [MathJax]/extensions/TeX/mathchoice.js

BZOJ4869: [Shoi2017]相逢是问候

BZOJ4869: [Shoi2017]相逢是问候

Description

Informatikverbindetdichundmich.
信息将你我连结。B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数。一共有m个操作,可以
分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是
输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为
这个结果可能会很大,所以你只需要输出结果mod p的值即可。

Input

第一行有三个整数n,m,p,c,所有整数含义见问题描述。
接下来一行n个整数,表示a数组的初始值。
接下来m行,每行三个整数,其中第一个整数表示了操作的类型。
如果是0的话,表示这是一个修改操作,操作的参数为l,r。
如果是1的话,表示这是一个询问操作,操作的参数为l,r。
1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p

Output

对于每个询问操作,输出一行,包括一个整数表示答案mod p的值。

Sample Input

4 4 7 2
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3

Sample Output

0
3

题解Here!

首先,有个题建议先做一做:

BZOJ3884: 上帝与集合的正确用法

然后你会发现你已经会了本题的重要思想:

c^{c^{c^{...}}}\equiv c^{(c^{c^{...}}\mod \varphi(p)+\varphi(p))}(\mod p)

当然,那个前提还是没变:c^{c^{...}}>\varphi(p)

然后递归计算即可。

但是问题又出来了,区间操作怎么办?

不怕!我们还有一大堆数据结构没用呢!

当然前提是有个题建议做一做:

BZOJ3211: 花神游历各国

然后我们就可以类似地发现:当指数层数达到一定数量时,c^{c^{c^{...}}}\mod p的值不再变化。

所以直接线段树暴力单点修改,达到临界条件就打个标记丢一边不管了。

可是,这玩意写完了,我们发现这玩意的复杂度惊人啊:O(m\log_2n\log_2^2p)

这个怎么办呢?

不怕,我们拿出终极优化——欧拉函数很少!

这个有什么用?

这意味着模数很少!

所以我们可以考虑将快速幂预处理一下,分成c^i\mod pc^{10000\times i}\mod p两部分。

查询的时候就直接把两块合并就好。

但是!欧拉定理的运用是有限制条件的!

所以我们还需要一个bool数组来记录这个条件是否满足。

于是我们省去了一个\log_2p

总复杂度O(m\log_2n\log_2p),可以通过此题。

附上奇丑无比的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define LSON rt<<1
#define RSON rt<<1|1
#define DATA(x) a[x].data
#define SIGN(x) a[x].times
#define LSIDE(x) a[x].l
#define RSIDE(x) a[x].r
#define MAXN 50010
#define MAXM 60
using namespace std;
int n,m,base,mod,min_times=0;
long long val[MAXN],phi[MAXM],pow_one[MAXN/5][MAXM],pow_two[MAXN/5][MAXM];
bool flag,flag_one[MAXN/5][MAXM],flag_two[MAXN/5][MAXM];
struct Segment_Tree{
    long long data;
    int times,l,r;
}a[MAXN<<2];
inline int read(){
    int date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    return date*w;
}
inline long long min(const long long &x,const long long &y){return x<y?x:y;}
long long mexp(long long a,long long b,long long c){
    long long s=1;
    while(b){
        if(b&1)s=s*a%c;
        a=a*a%c;
        b>>=1;
    }
    return s;
}
long long get_phi(long long x){
    long long limit=sqrt(x),s=x;
    for(long long i=2;i<=limit;i++){
        if(x%i==0){
            s=s*(i-1)/i;
            while(x%i==0)x/=i;
        }
    }
    if(x>1)s=s*(x-1)/x;
    return s;
}
void make(){
    int limit=10000;
    long long x=mod;
    phi[0]=mod;
    while(x!=1){
        x=get_phi(x);
        phi[++min_times]=x;
    }
    phi[++min_times]=1;
    for(int i=0;i<=min_times;i++){
        pow_one[0][i]=1;
        for(int j=1;j<=limit;j++){
            pow_one[j][i]=pow_one[j-1][i]*base;
            if(pow_one[j][i]>=phi[i]){
                pow_one[j][i]%=phi[i];
                flag_one[j][i]=true;
            }
            flag_one[j][i]|=flag_one[j-1][i];
        }
    }
    for(int i=0;i<=min_times;i++){
        pow_two[0][i]=1;
        flag_two[1][i]=flag_one[limit][i];
        for(int j=1;j<=limit;j++){
            pow_two[j][i]=pow_two[j-1][i]*pow_one[limit][i];
            if(pow_two[j][i]>=phi[i]){
                pow_two[j][i]%=phi[i];
                flag_two[j][i]=true;
            }
            flag_two[j][i]|=flag_two[j-1][i];
        }
    }
}
inline long long calculate(long long x,long long v){
    flag=false;
    long long p=x%10000,q=x/10000,s=pow_one[p][v]*pow_two[q][v];
    if(s>=phi[v]){
        s%=phi[v];
        flag=true;
    }
    flag|=flag_one[p][v]|flag_two[q][v];
    return s;
}
long long solve(long long x,int deep,int limit){
    flag=false;
    if(deep==limit){
        if(x>=phi[deep]){
            flag=true;
            x%=phi[deep];
        }
        return x;
    }
    long long s=solve(x,deep+1,limit);
    return calculate((flag?(s+phi[deep+1]):s),deep);
}
inline void pushup(int rt){
    DATA(rt)=(DATA(LSON)+DATA(RSON))%mod;
    SIGN(rt)=min(SIGN(LSON),SIGN(RSON));
}
void buildtree(int l,int r,int rt){
    LSIDE(rt)=l;RSIDE(rt)=r;
    if(l==r){
        DATA(rt)=val[l];
        SIGN(rt)=0;
        return;
    }
    int mid=l+r>>1;
    buildtree(l,mid,LSON);
    buildtree(mid+1,r,RSON);
    pushup(rt);
}
void update(int l,int r,int rt){
    if(SIGN(rt)>=min_times)return;
    if(LSIDE(rt)==RSIDE(rt)){
        SIGN(rt)++;
        DATA(rt)=solve(val[LSIDE(rt)],0,SIGN(rt));
        return;
    }
    int mid=LSIDE(rt)+RSIDE(rt)>>1;
    if(l<=mid&&SIGN(LSON)<min_times)update(l,r,LSON);
    if(mid<r&&SIGN(RSON)<min_times)update(l,r,RSON);
    pushup(rt);
}
long long query(int l,int r,int rt){
    long long ans=0;
    if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
    int mid=LSIDE(rt)+RSIDE(rt)>>1;
    if(l<=mid)ans=(ans+query(l,r,LSON))%mod;
    if(mid<r)ans=(ans+query(l,r,RSON))%mod;
    return ans;
}
void work(){
    int f,x,y;
    while(m--){
        f=read();x=read();y=read();
        if(f==0)update(x,y,1);
        else printf("%lld\n",query(x,y,1));
    }
}
void init(){
    n=read();m=read();mod=read();base=read();
    for(int i=1;i<=n;i++)val[i]=read();
    buildtree(1,n,1);
    make();
}
int main(){
    init();
    work();
    return 0;
}

 

posted @   符拉迪沃斯托克  阅读(177)  评论(0编辑  收藏  举报
编辑推荐:
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
阅读排行:
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法
Live2D
欢迎阅读『BZOJ4869: [Shoi2017]相逢是问候』
点击右上角即可分享
微信分享提示