题目描述
Informatik verbindet dich und mich.
信息将你我连结。
B 君希望以维护一个长度为
n
n
的数组,这 个数组的下标为从
1
1
到
n
n
的正整数。
一共有
m
m
个操作,可以分为两种:
0 l r:表示将第 l l 个到第 r r 个数 {al,al+1,...,ar} { a l , a l + 1 , . . . , a r } 中的每一个数 ai a i 替换为 cai c a i ,即 c c 的 ai a i 次方,其中 c c 是输入的一个常数,也就是执行赋值 ai←cai a i ← c a i 。1 l r:求第 l l 个到第 r r 个数的和,也就是输出:
∑i=lrai ∑ i = l r a i
因为这个结果可能会很大,所以你只需要输出结果 mod p mod p 的值即可。
输入格式
第一行有四个整数
n,m,p,c
n
,
m
,
p
,
c
所有整数含义见问题描述。
接下来一行
n
n
个整数,表示
a
a
数组的初始值。
接下来
m
m
行,每行三个整数,其中第一个整数表示了操作的类型。
如果是
0
0
的话,表示这是一个修改操作,操作的参数为
l,r
l
,
r
。
如果是
1
1
的话,表示这是一个询问操作,操作的参数为
l,r
l
,
r
。
输出格式
对于每个询问操作,输出一行,包括一个整数表示答案 mod p mod p 的值。
样例
样例输入 1
4 4 7 2
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3
样例输出 1
0
3
样例输入 2
1 40 19910626 2
0
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
0 1 1
1 1 1
样例输出 2
1
2
4
16
65536
11418102
18325590
13700558
13700558
13700558
13700558
13700558
13700558
13700558
13700558
13700558
13700558
13700558
13700558
13700558
数据范围与提示
对于
0%
0
%
的测试点,和样例一模一样;
对于另外
10%
10
%
的测试点,没有修改;
对于另外
20%
20
%
的测试点,每次修改操作只会修改一个位置(也就是
l=r
l
=
r
),并且每个位置至多被修改一次;
对于另外
10%
10
%
的测试点,
p=2
p
=
2
;
对于另外
10%
10
%
的测试点,
p=3
p
=
3
;
对于另外
10%
10
%
的测试点,
p=4
p
=
4
;
对于另外
20%
20
%
的测试点,
n≤100,m≤100
n
≤
100
,
m
≤
100
;
对于
100%
100
%
的测试点,
1≤n≤50000,
1
≤
n
≤
50000
,
1≤m≤50000,
1
≤
m
≤
50000
,
1≤p≤100000000,0<c<p,0≤ai<p
1
≤
p
≤
100000000
,
0
<
c
<
p
,
0
≤
a
i
<
p
。
题解
这道题是一个神题,正解很神,我的方法比较玄学。
首先,看到
cx
c
x
应该想到用欧拉降幂公式:
当且仅当 B≥p B ≥ p 时一定成立。
我们发现在不断进行 x←cx x ← c x 的操作时,指数用欧拉降幂公式处理后最终会保持不变 。
如此不断反复,直到 φ(…φ(p))=1 φ ( … φ ( p ) ) = 1 时,高层指数绘膜成 0 0 ,结果绘保持不变。
所以我们可以预处理出每一个数经过任意次赋值后得到的结果,直到结果保持不变后结束。我的方法是记忆化搜索, cal(x,cnt,p) c a l ( x , c n t , p ) 表示 x x 在进行 cnt c n t 次赋值后得到的结果 modp mod p 的值。判断指数是否大于膜数我使用的一种类似哈希的方法,计算两次快速幂,一遍膜对应的膜数,另一遍膜一个比膜数大的任意大质数(自然溢出绘被卡),比较两个结果是否相同。
我们把每一步得到的结果存下来,判断结果是否不变可以判断最后 x x ( x x 是一个常数,需要自行调参)位是否相同来实现。
然后维护数列可以使用线段树,修改时暴力修改区间里的每一个数,如果发现结果保持不变,就给该点打一个标记,发现当前区间所有节点都有标记就跳过这个区间,由降幂公式可以证明一个数修改的次数有限,可以保证复杂度。
总复杂度 O(nlog2n×玄学) O ( n log 2 n × 玄 学 ) 。
My Code
#include <bits/stdc++.h>
#define lc k << 1
#define rc k << 1 | 1
using namespace std;
typedef long long ll;
ll p, c;
int n, m;
ll pri[1000005]; int cntpri;
ll phi[10000005]; int vis[10000005];
ll t1[100005];
void shai(){
vis[1] = 1; phi[1] = 1;
for(int i = 2; i <= 10000000; i ++){
if(!vis[i]) pri[++cntpri] = i, phi[i] = i - 1;
for(int j = 1; j <= cntpri && i * pri[j] <= 10000000; j ++){
vis[i * pri[j]] = 1;
if(i % pri[j] == 0){
phi[i * pri[j]] = phi[i] * pri[j]; break;
}
phi[i * pri[j]] = phi[i] * (pri[j] - 1);
}
}
}
ll getphi(ll x){
if(x <= 10000000) return phi[x];
ll tmp = x, ret = 1;
for(int i = 1; pri[i] * pri[i] <= tmp; i ++){
if(tmp % pri[i] == 0){
tmp /= pri[i]; ret = ret * (pri[i] - 1);
while(tmp % pri[i] == 0){
tmp /= pri[i]; ret = ret * pri[i];
}
}
}
if(tmp > 1){
ret = ret * (tmp - 1);
}
return ret;
}
ll qpow(ll a, ll b, ll mod){
ll ret = 1;
for(; b; b >>= 1, a = a * a % mod){
if(b & 1) ret = ret * a % mod;
}
return ret;
}
ll qpow2(ll a, ll b){
ll ret = 1;
for(; b; b >>= 1, a = a * a % 998244353){
if(b & 1) ret = ret * a % 998244353;
}
return ret;
}
ll phip[10005];
int tot;
vector<ll> vec[50005];
int vi2[7][30];
pair<ll, int> fafa[7][30];
pair<ll, int> cal(ll x, int d0, int d1){
//printf("%lld %d %d\n", x, d0, d1);
if(d0 <= 7){
if(vi2[d0 - 1][d1]) return fafa[d0 - 1][d1];
vi2[d0 - 1][d1] = 1;
}
if(d0 == 1) {
ll tmp1 = qpow(c, x, phip[d1]);
ll tmp2 = qpow2(c, x);
if(tmp1 != tmp2) return fafa[d0 - 1][d1] = make_pair(tmp1, 1);
else return fafa[d0 - 1][d1] = make_pair(tmp1, 0);
}
pair<ll, int> tmp3 = cal(x, d0 - 1, d1);
if(tmp3.second == 1){
pair<ll, int> tmp4 = cal(x, d0 - 1, d1 + 1);
ll tmp1 = qpow(c, tmp4.first + phip[d1 + 1], phip[d1]);
ll tmp2 = qpow2(c, tmp4.first + phip[d1 + 1]);
if(d0 <= 7){
if(tmp1 != tmp2) return fafa[d0 - 1][d1] = make_pair(tmp1, 1);
else return fafa[d0 - 1][d1] = make_pair(tmp1, 0);
}else{
if(tmp1 != tmp2) return make_pair(tmp1, 1);
else return make_pair(tmp1, 0);
}
}else{
ll tmp1 = qpow(c, tmp3.first, phip[d1]);
ll tmp2 = qpow2(c, tmp3.first);
if(d0 <= 7){
if(tmp1 != tmp2) return fafa[d0 - 1][d1] = make_pair(tmp1, 1);
else return fafa[d0 - 1][d1] = make_pair(tmp1, 0);
}else{
if(tmp1 != tmp2) return make_pair(tmp1, 1);
else return make_pair(tmp1, 0);
}
}
}
void pre(int i){
memset(vi2, 0, sizeof(vi2));
vec[i].push_back(t1[i]);
vec[i].push_back(qpow(c, t1[i], p));
while(1){
ll tmp = cal(t1[i], vec[i].size(), 1).first;
vec[i].push_back(tmp);
//printf("%lld\n", tmp);
int mn = max(int(vec[i].size() - 3), 0);
if(mn == 0) continue;
int flag = 0;
for(int j = mn + 1; j < vec[i].size(); j ++){
if(vec[i][j] != vec[i][j - 1]){
flag = 1; break;
}
}
if(flag == 0) break;
}
}
struct node{
ll dat;
int dat2, tag;
int l, r;
};
struct seg{
node d[200005];
void pushup(int k){
d[k].dat = d[lc].dat + d[rc].dat;
if(d[k].dat >= p) d[k].dat -= p;
if(d[lc].tag && d[rc].tag) d[k].tag = 1;
}
void build(int k, int l, int r){
d[k].l = l; d[k].r = r;
d[k].dat = 0; d[k].dat2 = 0;
d[k].tag = 0;
if(l == r){
d[k].dat = t1[l];
if(d[k].dat2 == vec[d[k].l].size() - 1){
d[k].tag = 1;
}
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(k);
}
void modify(int k, int l, int r){
if(d[k].tag == 1) return;
if(d[k].l == d[k].r){
d[k].dat2++;
d[k].dat = vec[d[k].l][d[k].dat2];
if(d[k].dat2 == vec[d[k].l].size() - 1){
d[k].tag = 1;
}
return;
}
int mid = (d[k].l + d[k].r) >> 1;
if(l <= mid) modify(lc, l, r);
if(r > mid) modify(rc, l, r);
pushup(k);
}
ll query(int k, int l, int r){
if(l <= d[k].l && d[k].r <= r){
return d[k].dat;
}
ll sum = 0;
int mid = (d[k].l + d[k].r) >> 1;
if(l <= mid) sum += query(lc, l, r);
if(r > mid) sum += query(rc, l, r);
return sum % p;
}
}Seg;
int main(){
scanf("%d%d%lld%lld", &n, &m, &p, &c);
shai();
for(int i = 1; i <= n; i ++){
scanf("%lld", &t1[i]);
}
phip[++tot] = p;
for(int i = 1; i <= 1000; i ++){
tot++;
phip[tot] = getphi(phip[tot - 1]);
}
for(int i = 1; i <= n; i ++){
pre(i);
}
Seg.build(1, 1, n);
for(int i = 1; i <= m; i ++){
int opt, l, r;
scanf("%d%d%d", &opt, &l, &r);
if(opt == 0){
Seg.modify(1, l, r);
}else{
printf("%lld\n", Seg.query(1, l, r));
}
}
return 0;
}
本文介绍了一种涉及数组操作的问题及其高效的解决方法。通过利用欧拉降幂公式和线段树等数据结构,实现对数组元素的快速修改与查询。文章详细解释了算法原理,并提供了具体的代码实现。
1046

被折叠的 条评论
为什么被折叠?



