P4681 [THUSC2015] 平方运算
题意
给定长度为 的序列和一个数 ,有 个操作:
1 l r
:将区间 内的数分别平方对 取模。2 l r
:求区间 内的数的和。
分析
看到修改如此诡异,考虑分块。
注意到值域很小,且 有时不是质数,这启发我们要从打表开始着手。
经过打表可得,对于题目中的所有 ,每个数进行平方运算的循环节长度 ,一个数要进入循环需要的次数 ,且对于同一个 ,任意两个不同的数的循环节 满足 。于是我们只要知道对于一个 的最大循环节长度,将其作为每个数的循环节长度即可。
为了优化常数,可以预处理出每个数乘方若干次后的值。然后就是众所周知的操作:
- 对于散块,查询和修改都直接暴力即可,同时维护 表示块 修改 次后的和。
- 对于整块,查询直接 回答,修改则要分两种情况:若该块修改次数不超过 ,说明可能有的数还没进入循环,当成散块暴力改;若超过 次,直接 改变标记即可。
等等,这样复杂度好像要带上 的常数?那不炸飞了,考虑优化。
我们发现散块的复杂度偏高了,于是可以改为在暴力的同时,只维护 ,当后面要用到 时,重新求一下即可。感觉比较玄学,但是好像能均摊的样子,于是就过了。(
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
long long x=0,f=1;char ch=getchar();
while(!isdigit(ch))
{if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void write(long long x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=1e5+10,sN=330,S=1e4+10;
int n,m,p,len,nxt[S][61],a[N],tim[sN],lim[sN];
int id,pri[21]={233,2332,5,8192,23,45,37,4185,5850,2975
,2542,2015,2003,2010,4593,4562,1034,5831,9905,9977};
bool vis[N],fl[N];
int cnt[sN],tag[sN];
bool dos[sN][61];
int sum[sN][61],now[sN];
void init(){
tim[0]=28;lim[0]=3;
tim[1]=12;lim[1]=2;
tim[2]=1;lim[2]=2;
tim[3]=1;lim[3]=11;
tim[4]=10;lim[4]=1;
tim[5]=2;lim[5]=2;
tim[6]=6;lim[6]=2;
tim[7]=12;lim[7]=2;
tim[8]=4;lim[8]=2;
tim[9]=4;lim[9]=4;
tim[10]=4;lim[10]=3;
tim[11]=4;lim[11]=2;
tim[12]=60;lim[12]=1;
tim[13]=10;lim[13]=2;
tim[14]=24;lim[14]=1;
tim[15]=36;lim[15]=3;
tim[16]=44;lim[16]=1;
tim[17]=42;lim[17]=4;
tim[18]=46;lim[18]=2;
tim[19]=60;lim[19]=1;
}
void solve(int u){//打表
int P=pri[u];
int mx=0,m2=0;
memset(fl,0,sizeof(fl));
for(int i=1;i<P;i++){
memset(vis,0,sizeof(vis));
int j=i%P;
int len=0;
while(!vis[j]){
len++;
vis[j]=1;
j=j*j%P;
}
if(j==i)fl[len]=1,mx=max(mx,len);
m2=max(m2,len);
}
printf("tim[%d]=%d;lim[%d]=%d;\n",u,mx,u,m2-mx);
}
void pushdown(int k){
if(tag[k]){
for(int i=max(k*len,1);i<=min(n,k*len+len-1);i++){
a[i]=nxt[a[i]][tag[k]];
}
tag[k]=0;
}
}
void pushup(int k){
memset(dos[k],0,sizeof(dos[k]));
memset(sum[k],0,sizeof(sum[k]));
dos[k][0]=1;
for(int i=max(k*len,1);i<=min(n,k*len+len-1);i++){
sum[k][0]+=a[i];
}
}
void change(int l,int r){
int kl=l/len,kr=r/len;
if(kl==kr){
pushdown(kl);
for(int i=l;i<=r;i++){
a[i]=nxt[a[i]][1];
}
pushup(kl);
return;
}
change(l,(kl+1)*len-1);
change(kr*len,r);
for(int i=kl+1;i<kr;i++){
if(cnt[i]<=lim[id]){
cnt[i]++;
change(i*len,(i+1)*len-1);
continue;
}
tag[i]=(tag[i]+1)%tim[id];
}
}
int ask(int l,int r){
int kl=l/len,kr=r/len;
int ans=0;
if(kl==kr){
pushdown(kl);
for(int i=l;i<=r;i++){
ans+=a[i];
}
pushup(kl);
return ans;
}
ans=ask(l,(kl+1)*len-1)+ask(kr*len,r);
for(int i=kl+1;i<kr;i++){
if(!dos[i][tag[i]]){
dos[i][tag[i]]=1;
for(int j=max(i*len,1);j<=min(n,i*len+len-1);j++){
sum[i][tag[i]]+=nxt[a[j]][tag[i]];
}
}
ans+=sum[i][tag[i]];
}
return ans;
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n=read();m=read();p=read();
len=sqrt(n);
init();
for(int i=0;i<20;i++){
if(p==pri[i]){
id=i;
}
}
for(int i=1;i<p;i++){
nxt[i][0]=i;
}
for(int i=1;i<=60;i++){
for(int j=1;j<p;j++){
nxt[j][i]=nxt[j][i-1];
nxt[j][i]=(nxt[j][i]*nxt[j][i])%p;
}
}
for(int i=1;i<=n;i++){
a[i]=read();
}
for(int k=0;k<=n/len;k++){
pushup(k);
}
for(int i=1;i<=m;i++){
int op=read(),l=read(),r=read();
if(op==1){
change(l,r);
}
else {
write(ask(l,r));
puts("");
}
}
return 0;
}
本文作者:luckydrawbox
本文链接:https://www.cnblogs.com/luckydrawbox/p/18526441
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步