搜分块进来,于是就打了分块,结果卡了一下变成了170ms?!
code在下面,不知道是不是少考虑了什么,求dalao康康
#include<bits/stdc++.h>
using namespace std;
#define getchar() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?EOF:*S++)
namespace get_out
{
char B[1<<15],*S=B,*T=B;
char op;
inline void read_(int &x)
{
x=0;
int fi(1);
op=getchar();
while((op<'0'||op>'9')&&op!='-') op=getchar();
if(op=='-') op=getchar(),fi=-1;
while(op>='0'&&op<='9') x=(x<<1)+(x<<3)+(op^48),op=getchar();
x*=fi;
return;
}
inline void read_(long long &x)
{
x=0;
int fi(1);
op=getchar();
while((op<'0'||op>'9')&&op!='-') op=getchar();
if(op=='-') op=getchar(),fi=-1;
while(op>='0'&&op<='9') x=(x<<1)+(x<<3)+(op^48),op=getchar();
x*=fi;
return;
}
inline void read_(double &x)
{
x=0.0;
float fi(1.0),sm(0.0);
op=getchar();
while((op<'0'||op>'9')&&op!='-') op=getchar();
if(op=='-') op=getchar(),fi=-1.0;
while(op>='0'&&op<='9') x=(x*10.0)+(op^48),op=getchar();
if(op=='.') op=getchar();
while(op>='0'&&op<='9') sm=(sm*10.0)+(op^48),op=getchar();
while(sm>1.0) sm/=10.0;
x+=sm,x*=fi;
return;
}
inline void write_(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write_(x/10);
putchar(x%10+'0');
}
inline void write_(long long x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write_(x/10);
putchar(x%10+'0');
}
}
using namespace get_out;
//读写优化
#define maxn 200005
#define mod 998244353
class a_block
{
public:
int maxx;//区间最大值
int cnt;//这个区间有多少次平方操作
int l,r;//左界,右界
};
a_block block[2000];//块
int tot;//块数
int n,m;//
int block_len;//块长
int a[maxn],of[maxn];//值,所属的块
int cnt[maxn];//单独计数每个点除区间外的平方次数(两边总得暴力)
inline void renew(int pos)
{
block[pos].maxx=0;
for(int j(block[pos].l);j<=block[pos].r;++j)
if(block[pos].maxx<a[j])
block[pos].maxx=a[j];
}//暴力刷新区间最大值
inline void downput(int pos)
{
--block[pos].cnt;
for(int j(block[pos].l);j<=block[pos].r;++j)
++cnt[j];
}//开方时可能需要下放标记的平方次数,暴力下放
inline void solve(void)
{
for(register int i(1),l,r,k,posl,posr;i<=m;++i)
{
read_(k),read_(l),read_(r);
posl=of[l],posr=of[r];
if(k==1)//开方
{
if(posl==posr)//同一区间
{
if(block[posl].cnt==0)
for(int j(l);j<=r;++j)
if(cnt[j])
--cnt[j];
else
a[j]=floor(sqrt(a[j]));
else
{
--block[posl].cnt;
for(int j(block[posl].l);j<l;++j)
++cnt[j];
for(int j(r+1);j<=block[posl].r;++j)
++cnt[j];
}//要下放标记,因为是不可逆的(有取整)
renew(posl);//开方后重算最大值
}
else
{
bool ifl(0),ifr(0);//是否需要重置最大值
if(block[posl].maxx>1)//容易知道最大值小于等于1时任何操作没有意义
{
if(block[posl].cnt)
downput(posl);
for(int j(l);j<=block[posl].r;++j)
if(a[j]>1)
if(cnt[j])
--cnt[j];
else
a[j]=floor(sqrt(a[j])),ifl=1;
}
if(block[posr].maxx>1)
{
if(block[posr].cnt)
downput(posr);
for(int j(block[posr].l);j<=r;++j)
if(a[j]>1)
if(cnt[j])
--cnt[j];
else
a[j]=floor(sqrt(a[j])),ifr=1;
}
for(int j(posl+1);j<posr;++j)
if(block[j].maxx>1)
{
if(block[j].cnt)
--block[j].cnt;//使用标记
else//没有标记就暴力开方
{
for(int k(block[j].l);k<=block[j].r;++k)
if(cnt[k])
--cnt[k];
else
a[k]=floor(sqrt(a[k]));
renew(j);//开方后重算最大值
}
}
if(ifl) renew(posl);//开方后重算最大值
if(ifr) renew(posr);//开方后重算最大值
}
}
else
{
if(posl==posr)
{
for(int j(l);j<=r;++j)
++cnt[j];
//renew(posl);
//只有开方会影响最大值,此时的cnt虽然可能改变maxx的值,但我们只关心最大值是否大于1,所以是正确的
}
else
{
if(block[posl].maxx>1)
for(int j(l);j<=block[posl].r;++j)
++cnt[j];
if(block[posr].maxx>1)
for(int j(block[posr].l);j<=r;++j)
++cnt[j];
for(int j(posl+1);j<posr;++j)
if(block[j].maxx>1)
++block[j].cnt;
//renew(posl),renew(posr);
}
}
}
}
#define LL long long
inline LL fast_(LL b,LL p,LL MOD_LL)
{
LL c=b,ans=1LL;
while(p)
{
if(p&1) ans*=c%MOD_LL,ans%=MOD_LL;
c*=c,c%=MOD_LL,p>>=1;
}
return ans;
}//取模快速幂
signed main()
{
read_(n),read_(m);
block_len=pow(n,0.45)+10;//块长
const double comp=log(mod-1),xs=log(2);
for(register int i(1);i<=n;++i)
read_(a[i]);
//读入
for(int i(1);i<=n;i+=block_len)
{
block[++tot].l=i,block[tot].r=min(i+block_len-1,n);
for(int j(i);j<=block[tot].r;++j)
{
of[j]=tot;
if(block[tot].maxx<a[j])
block[tot].maxx=a[j];
}
}//分块
solve();//处理
LL ans(0);
for(register int i(1);i<=n;++i)
if(xs*(cnt[i]+block[of[i]].cnt)<comp)
ans=(ans+fast_(a[i],fast_(2,(LL)(cnt[i]+block[of[i]].cnt),mod-1),mod))%mod;
else
ans=(ans+fast_(a[i],mod-1+fast_(2,(LL)(cnt[i]+block[of[i]].cnt),mod-1),mod))%mod;
//拓展欧拉定理算最后结果
write_(ans),putchar('\n');
return 0;
}