【bzoj4869】相逢是问候
Solution
这道题的话。。长得就是线段树的样子qwq
如果做过的话。。可能会联想到bzoj3211(没写博qwq晚点再说吧哈哈。。)
首先大胆猜一波结论:这题跟3211一样也是修改到了一定程度就不会再有变化了!那然后写起来就是线段树暴力修改然后如果整个区间达到了修改上限的话那就不走了
然而这个上限是啥呢。。
这里还是要用到扩展欧拉定理
\begin{cases}
&a^{b\%\varphi(p)} &\gcd(a,p)=1\\
&a^b &\gcd(a,p)\neq1,b<\phi(p)\\
&a^{b\%\varphi(p)+\varphi(p)} &\gcd(a,p)\neq1,b\geq\phi(p)
\end{cases}\pmod p
\]
(然而实际上在这题里面第一条并没有什么用毕竟gcd(a,p)=1这个限制有点令人难受qwq)
然后接着我们来看一下这个修改操作
经过多次修改之后应该是长成一堆\(c\)指数翻上去,然后最后那个指数是\(c^a_i\)这个样子(有没有长得很像上帝和集合(bzoj3884)那道题的那一堆\(2\)?)
然后我们就用类似bzoj3884中的处理方法,大力降幂,跟那题一样,当\(\varphi(p)\)降到\(1\)了之后\(b%\varphi(1)+\varphi(1)=1\),也就是说接下来无论再怎么搞都不会再变了(每个数最多被修改\(log\)次就不会再变了,具体为啥的话在bzoj3884里面有写就不提了)
与那题不同的是,这里需要判断指数与模数的大小关系来判断能否降成\(b\% \varphi(p)+\varphi(p)\)
然而这题写起来其实。。有点恶心
具体一点的话就是:
首先我们预处理出\(p\)被取\(\varphi\)多少次之后会变成\(1\),记这个次数为\(Mx\),并且将中间每一步的结果记录在一个数组里面,方便我们后面求值的时候直接调用作为模数
然后我们开一棵线段树,对于修改操作,我们在每个区间记录一个\(sum[x]\)和一个\(mn[x]\),前者表示答案,后者表示这个区间中被修改最少的那个位置被修改了多少次
在修改的时候,如果说当前区间的\(mn[x]>Mx\)那么说明这个区间所有的位置都已经达到上限可以直接跳过不用管了,否则递归进去处理,一直到叶子节点。叶子节点的话,每次修改的时候能直接改的只是这个节点的\(mn[x]\),而单点的\(sum[x]\)则要重新再算一次
至于怎么计算\(sum[x]\)的话,可以考虑从降幂的最后一层层推回来,然而因为每次我们推出来的都是下一层的指数部分,如果用最暴力的快速幂求解的话,加上层数最多是\(log\)以及线段树的一个\(log\),总的复杂度会变成\(O(nlog^3n)\),在TLE的边缘试探。。
然后我们注意到每次快速幂底数其实都是\(c\),所以我们可以考虑将\(c\)的一些次方预处理出来,这样就可以直接调用了
然而这里有一个小问题,次方数。。最大可以去到\(10^8\),所以这里要用一个小trick:
我们预处理出\(c\)的\(1-10000\)次方以及\(c^{10000}\)的\(1-10000\)次方,这样对于一个指数,大于\(10^4\)的部分和小于\(10^4\)的部分分开来调用就好了,然后就可以十分愉快地省掉一个\(log\),\(O(nlog^2n)\)相对来说就优秀很多啦
此外还有一个比较麻烦的地方是,我们需要判断指数和\(p\)的大小,注意,这里的指数是不能取模的,那就有点糟糕了qwq
这里的解决方案是,注意到由于我们是倒过来推的,这里的指数在用扩展欧拉定理降幂之后会变成\(c\)的若干次方,所以我们可以先预处理出\(c\)的若干次方的值(处理到这个值\(>10^9\)就可以了,也可以更加小一点,反正\(>10^8\)就行),对于\(>10^8\)的部分我们直接打上一个\(tag\)(代码里面我是直接将表示当前幂指数的那个变量赋成\(-1\)),然后根据是否有\(tag\)判断是否能够用扩展欧拉定理的第三条就好了
细节什么的比较。。嗯qwq(调试调了不知道多久最后终于调出来了发现是判断的时候下标忘记+1也是很开心。。。)
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int MAXN=50010,SEG=MAXN*4;
int mod[MAXN],prime[MAXN];
int pwc[10010][30],pwc1[10010][30],pc[60];
bool vis[MAXN];
int n,m,P,c,Mx,Up=10000,ans;
int a[MAXN];
namespace Seg{/*{{{*/
int ch[SEG][2],sum[SEG],mi[SEG];
int n,tot;
void pushup(int x){
sum[x]=(1LL*sum[ch[x][0]]+sum[ch[x][1]])%P;
mi[x]=min(mi[ch[x][0]],mi[ch[x][1]]);
}
void _build(int x,int l,int r){
sum[x]=0; mi[x]=0;
if (l==r) {sum[x]=a[l];return;}
int mid=l+r>>1;
ch[x][0]=++tot; _build(ch[x][0],l,mid);
ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
pushup(x);
}
void build(int _n){n=_n;tot=1;_build(1,1,n);}
int powc(int Pw,int Mod){return 1LL*pwc1[Pw/Up][Mod]*pwc[Pw%Up][Mod]%mod[Mod];}
int calc(int loc,int Pw){
int nowmi=a[loc],ret=a[loc];
for (int i=Pw-1;i>=0;--i){
if (nowmi==-1||nowmi>=mod[i+1]){
ret=powc(ret%mod[i+1]+mod[i+1],i);
nowmi=-1;
}
else{
ret=powc(ret,i);
if (nowmi<60) nowmi=pc[nowmi];
else nowmi=-1;
}
}
return ret;
}
void _update(int x,int l,int r,int lx,int rx){
if (mi[x]>=Mx) return;
if (lx==rx){
++mi[x];
sum[x]=calc(lx,mi[x]);
return;
}
int mid=lx+rx>>1;
if (l<=mid) _update(ch[x][0],l,r,lx,mid);
if (r>mid) _update(ch[x][1],l,r,mid+1,rx);
pushup(x);
}
void update(int l,int r){_update(1,l,r,1,n);}
int _query(int x,int l,int r,int lx,int rx){
if (l<=lx&&rx<=r) return sum[x];
int mid=lx+rx>>1,ret=0;
if (l<=mid) ret=(1LL*ret+_query(ch[x][0],l,r,lx,mid))%P;
if (r>mid) ret=(1LL*ret+_query(ch[x][1],l,r,mid+1,rx))%P;
return ret;
}
int query(int l,int r){return _query(1,l,r,1,n);}
}/*}}}*/
void prework(int n);
int phi(int x);
int ksm(int x,int y,int p);
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int op,l,r;
scanf("%d%d%d%d",&n,&m,&P,&c);
for (int i=1;i<=n;++i) scanf("%d",a+i);
prework(MAXN);
Seg::build(n);
for (int i=1;i<=m;++i){
scanf("%d%d%d",&op,&l,&r);
if (op==0)
Seg::update(l,r);
else{
ans=Seg::query(l,r);
printf("%d\n",ans);
}
}
}
void prework(int n){
int cnt=0;
for (int i=2;i<n;++i){
if (!vis[i])
prime[++cnt]=i;
for (int j=1;j<=cnt&&prime[j]*i<n;++j){
vis[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
mod[0]=P;Mx=0;
while (mod[Mx]!=1) ++Mx,mod[Mx]=phi(mod[Mx-1]);
mod[++Mx]=1;
for (int i=0;i<=Up;++i)
for (int j=0;j<=Mx;++j)
pwc[i][j]=ksm(c,i,mod[j]);
for (int i=0;i<=Up;++i)
for (int j=0;j<=Mx;++j)
pwc1[i][j]=ksm(ksm(c,Up,mod[j]),i,mod[j]);
//...?
pc[0]=1;
int flag=100000;
for (int i=1;i<60;++i){
if (pc[i-1]==-1) pc[i]=-1;
if (1LL*pc[i-1]*c<=1000000000)
pc[i]=pc[i-1]*c;
else
pc[i]=-1,flag=min(flag,i);
}
}
int phi(int x){
int ret=x;
for (int i=1;prime[i]*prime[i]<=x;++i){
if (x%prime[i]) continue;
ret=ret-ret/prime[i];
while (x%prime[i]==0) x/=prime[i];
}
if (x>1) ret=ret-ret/x;
return ret;
}
int ksm(int x,int y,int p){
int ret=1,base=x;
for (;y;y>>=1,base=1LL*base*base%p)
if (y&1) ret=1LL*ret*base%p;
return ret;
}
【bzoj4869】相逢是问候的更多相关文章
- 洛谷P3747 [六省联考2017]相逢是问候
传送门 题解 扩展欧拉定理. 线段树维护,已经全改到底了的节点就不管,不然暴力修改下去. //Achen #include<algorithm> #include<iostream& ...
- 【BZOJ4869】相逢是问候(线段树,欧拉定理)
[BZOJ4869]相逢是问候(线段树,欧拉定理) 题面 BZOJ 题解 根据欧拉定理递归计算(类似上帝与集合的正确用法) 所以我们可以用线段树维护区间最少的被更新的多少次 如果超过了\(\varph ...
- [BZOJ4869][六省联考2017]相逢是问候(线段树+扩展欧拉定理)
4869: [Shoi2017]相逢是问候 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 1313 Solved: 471[Submit][Stat ...
- 【BZOJ4869】相逢是问候 [线段树][欧拉定理]
相逢是问候 Time Limit: 40 Sec Memory Limit: 512 MB[Submit][Status][Discuss] Description Informatikverbin ...
- BZOJ:4869: [Shoi2017]相逢是问候
4869: [Shoi2017]相逢是问候 先说点正经的…… 显然做了有限次(我只知道是有限次,而且不会大,别人说是log次?)修改以后会达到不动点,即以后怎么修改都不变了. 然后就随便做了.(3个l ...
- bzoj 4869: [Shoi2017]相逢是问候 [扩展欧拉定理 线段树]
4869: [Shoi2017]相逢是问候 题意:一个序列,支持区间\(a_i \leftarrow c^{a_i}\),区间求和.在模p意义下. 类似于开根操作,每次取phi在log次后就不变了. ...
- 洛谷 P3747 [六省联考2017]相逢是问候 解题报告
P3747 [六省联考2017]相逢是问候 题目描述 \(\text {Informatik verbindet dich und mich.}\) 信息将你我连结. \(B\) 君希望以维护一个长度 ...
- 2017 [六省联考] T2 相逢是问候
4869: [Shoi2017]相逢是问候 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 1205 Solved: 409[Submit][Stat ...
- BZOJ4869 六省联考2017相逢是问候(线段树+欧拉函数)
由扩展欧拉定理,a^(a^(a^(……^x)))%p中x作为指数的模数应该是φ(φ(φ(φ(……p)))),而p取log次φ就会变为1,也即每个位置一旦被修改一定次数后就会变为定值.线段树维护区间剩余 ...
- BZOJ4869 [Shoi2017]相逢是问候 【扩展欧拉定理 + 线段树】
题目链接 BZOJ4869 题解 这题调得我怀疑人生,,结果就是因为某些地方\(sb\)地忘了取模 前置题目:BZOJ3884 扩展欧拉定理: \[c^a \equiv c^{a \mod \varp ...
随机推荐
- python-编程从入门到实践
python-编程从入门到实践 1.python文件后缀名: .py 是Python的源码文件,由Python.exe解释. .pyc 是Python的编译文件.pyc 文件往往代替 py 文件发布: ...
- 深入了解MySQL存储索引
(一)关于存储引擎 创建合适的索引是SQL性能调优中最重要的技术之一.在学习创建索引之前,要先了解MySql的架构细节,包括在硬盘上面如何组织的,索引和内存用法和操作方式,以及存储引擎的差异如何影响到 ...
- rz和sz上传下载文件
安装软件包 yum install lrzsz 上传文件,输入rz选择文件上传(可以按住shift键多选) # rz sz 下载文件到本地,选择保存文件夹 # sz dd xshell设 ...
- gets函数的完美替代
众所周知 在C语言中scanf用来读取一行字符串时遇到空格或回车会停止 而若要读入一行带空格的字符串时 有些人会用gets来代替 然而,gets的最大问题在于:会读取超过数组长度上限个字符,而超出长度 ...
- 微软职位内部推荐-Software Engineer II_HPC
微软近期Open的职位: Job Title: Software Engineer II_HPC Location: Shanghai, China Are you passionate about ...
- linux同步软件
linux同步软件:scp,rsync,inotify,sersync 1.scp: scp就是secure copy,是用来进行远程文件拷贝的.数据传输使用 ssh,并且和ssh 使用相同的认证方式 ...
- sql server block如何查询并kill
本帖提供两种做法,可避免在 SQL Server 事务锁定时产生的不正常或长时间阻塞,让用户和程序也无限期等待,甚至引起 connection pooling 连接数超过容量. 所谓的「阻塞」,是指当 ...
- 检查Linux服务器性能的关键十条命令
检查Linux服务器性能的关键十条命令 概述 通过执行以下命令,可以在1分钟内对系统资源使用情况有个大致的了解. uptime dmesg | tail vmstat 1 mpstat -P ALL ...
- Scrum立会报告+燃尽图(十月二十四日总第十五次)
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2284 项目地址:https://git.coding.net/zhang ...
- c# 修改pdf
继续引用spire的dll. 1.代码如下: PdfDocument doc = new PdfDocument(); doc.LoadFromFile("wen.pdf"); P ...