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
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3
Sample Output
0
3
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 p和c^{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; } |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法