题解:P7334 [JRKSJ R1] 吊打
DX3906_ourstar · · 题解
本 DS 领域萌新花了 4h 才切掉此题,遂写篇题解纪念一下。
分析
首先需要明确一点:这题不能直接维护原序列,因为平方时不能直接取模,这就导致结果会非常大,很容易溢出。
为什么不能直接取模呢?举个例子,对
那么考虑不直接维护原序列该怎么做。暂时不管开根,只考虑平方。设
于是来考虑有开根该怎么做。还是本着维护
-
若
a_i\le1 ,跳过; -
若
cnt_i=0 ,a_i\leftarrow \lfloor\sqrt{a_i}\rfloor ; -
若
cnt_i\ge1 ,cnt_i \leftarrow cnt_i-1 。
然后考虑对于整块该怎么处理。我们设
-
若第
i 块全为1 ,跳过; -
若
mn_i=0 ,直接把第i 块当作散块处理(由势能分析,这一步的复杂度是有保障的); -
否则,将第
i 块上的所有cnt_j\leftarrow cnt_j-1 。
但接下来,我们还有一个问题没有解决——查询时,作为
这时候就需要一点——数学技巧!
我们希望找到一种手段,可以把
降幂?有没有觉得很熟悉——没错,扩展欧拉定理!
由扩展欧拉定理,我们知道,当
而此题中,
于是我们就可以愉快地 AC 了!
代码
代码中的变量名与上面使用的名称保持一致,(应该)很好看懂。
#include<iostream>
#include<cmath>
#define ll unsigned long long
using namespace std;
const int N=2e5+5;
const int M=1e3+5;
const ll p=998244353;
int n,m;
ll ans;
int a[N];
namespace OIfast{
char buf[1<<21],*p1,*p2,*top, buffer[1<<21];
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?0:*p1++)
#define gc getchar()
inline int read(){
int n=0;char c=gc;
while(!isdigit(c))c=gc;
while(isdigit(c))n=(n<<3)+(n<<1)+(c^48),c=gc;
return n;
}
}using namespace OIfast;
inline ll qpow(ll a,int b,ll mod){
ll res=1;
while(b){
if(b&1)(res*=a)%=mod;
b>>=1,(a*=a)%=mod;
}
return res;
}
namespace FK{
int k/*块长*/,tot/*块数*/;
int cnt[N]/*平方次数*/;
int L[M]/*L[i] -> 第 i 块的左端点*/,R[M]/*R[i] -> 第 i 块的右端点*/,loc[N]/*loc[i] -> 第 i 个数在第几块*/,mn[M]/*cnt*/,la[M]/*cnt*/;
bool flag[M]/*是否全为 1*/;
inline void pushup(int i){
mn[i]=1e9,flag[i]=1;for(int j=L[i];j<=R[i];++j)mn[i]=min(mn[i],cnt[j]),flag[i]&=(a[j]<=1);
return ;
}
inline void pushdown(int i){
for(int j=L[i];j<=R[i];++j)cnt[j]+=la[i];
return la[i]=0,void();
}
inline void init(){
k=sqrt(n),tot=ceil((1.0*n)/(1.0*k));
for(int i=1;i<=tot;++i){
L[i]=(i-1)*k+1,R[i]=min(n,L[i]+k-1);
for(int j=L[i];j<=R[i];++j)loc[j]=i;
pushup(i);
}
return ;
}
inline void sk(int l,int r){
pushdown(loc[l]);
for(int i=l;i<=r;++i){
if(a[i]<=1)continue ;
if(!cnt[i])a[i]=sqrt(a[i]);
else --cnt[i];
}
return pushup(loc[l]),void();
}
inline void upd1(int l,int r){//开根
if(loc[l]==loc[r])sk(l,r);
else{
sk(l,R[loc[l]]),sk(L[loc[r]],r);
for(int i=loc[l]+1;i<=loc[r]-1;++i){
if(flag[i])continue ;
if(mn[i]+la[i]<=1)sk(L[i],R[i]);
else --la[i],--mn[i];
}
}
return ;
}
inline void upd2(int l,int r){//平方
if(loc[l]==loc[r]){
pushdown(loc[l]);for(int i=l;i<=r;++i)++cnt[i];pushup(loc[l]);
}else{
pushdown(loc[l]);for(int i=l;i<=R[loc[l]];++i)++cnt[i];pushup(loc[l]);
pushdown(loc[r]);for(int i=L[loc[r]];i<=r;++i)++cnt[i];pushup(loc[r]);
for(int i=loc[l]+1;i<=loc[r]-1;++i)++la[i],++mn[i];
}
return ;
}
}using namespace FK;
inline void work(){
int op=read(),l=read(),r=read();
return op==1?upd1(l,r):upd2(l,r),void();
}
signed main(){
n=read(),m=read();for(int i=1;i<=n;++i)a[i]=read();
init();while(m--)work();
for(int i=1;i<=tot;++i)pushdown(i);
for(int i=1;i<=n;++i)(ans+=qpow(a[i],qpow(2,cnt[i],p-1),p))%=p;
return printf("%lld\n",ans),0;
}