【XSY3938】平方问题(线段树)

本文介绍了一种使用线段树优化计算的方法,针对给定的模幂问题,通过观察指数的二进制表示和998244353模下的周期规律,将计算复杂度降低。通过构造线段树结构,实现了对24位循环的快速处理,大大提升了求解效率。
摘要由CSDN通过智能技术生成

题面

平方问题

题解

p = 998244353 p=998244353 p=998244353,那么 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\pmod p ap11(modp)。( gcd ⁡ ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1

那么 a b ≡ a b   m o d   ( p − 1 ) ( m o d p ) a^b\equiv a^{b\bmod (p-1)}\pmod p ababmod(p1)(modp)

题目的操作相当于每次把一个数的指数乘上 2 2 2,那么每个数都能表示成 a 2 x a^{2^x} a2x 的形式。(初始时 x = 0 x=0 x=0

打表找 2 x   m o d   998244352 2^x\bmod 998244352 2xmod998244352 的循环节,发现从 2 23 2^{23} 223 开始,每 24 24 24 位循环一次,即 2 23 ≡ 2 47 ( m o d p − 1 ) , 2 24 ≡ 2 48 ( m o d p − 1 ) , ⋯ 2^{23}\equiv 2^{47}\pmod{p-1},2^{24}\equiv 2^{48}\pmod{p-1},\cdots 223247(modp1),224248(modp1),

那么对于每个数的修改,前 23 23 23 次我们暴力跑,后面的我们就在线段树上对于每一个区间维护一个大小为 24 24 24 的环,修改一次我们就把环转一格即可。

代码如下:

#include<bits/stdc++.h>

#define N 200010

using namespace std;

namespace modular
{
	const int mod=998244353;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
	inline int mul(int x,int y){return 1ll*x*y%mod;}
}using namespace modular;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,q,a[N],tim[N];
int f[N<<2][24];
int lazy[N<<2];
bool ok[N<<2];

void up(int k)
{
	for(int i=0;i<24;i++) f[k][i]=add(f[k<<1][i],f[k<<1|1][i]);
	ok[k]=(ok[k<<1]&ok[k<<1|1]);
}

void build(int k,int l,int r)
{
	if(l==r)
	{
		f[k][0]=read();
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	up(k);
}

void turn(int k,int tim)
{
	static int b[24];
	for(int i=0;i<24;i++) b[i]=f[k][i];
	for(int i=0;i<24;i++) f[k][i]=b[(i+tim)%24];
}

void downn(int k,int tim)
{
	turn(k,tim);
	lazy[k]=(lazy[k]+tim)%24;
}

void down(int k)
{
	if(lazy[k])
	{
		downn(k<<1,lazy[k]);
		downn(k<<1|1,lazy[k]);
		lazy[k]=0;
	}
}

void update(int k,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr&&ok[k])
	{
		downn(k,1);
		return;
	}
	if(l==r)
	{
		f[k][0]=mul(f[k][0],f[k][0]);
		tim[l]++;
		if(tim[l]==23)
		{
			ok[k]=1;
			for(int i=1;i<24;i++)
				f[k][i]=mul(f[k][i-1],f[k][i-1]);
		}
		return;
	}
	down(k);
	int mid=(l+r)>>1;
	if(ql<=mid) update(k<<1,l,mid,ql,qr);
	if(qr>mid) update(k<<1|1,mid+1,r,ql,qr);
	up(k);
}

int query(int k,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr) return f[k][0];
	down(k);
	int mid=(l+r)>>1,ans=0;
	if(ql<=mid) ans=add(ans,query(k<<1,l,mid,ql,qr));
	if(qr>mid) ans=add(ans,query(k<<1|1,mid+1,r,ql,qr));
	return ans;
}

int main()
{
	n=read(),q=read();
	build(1,1,n);
	while(q--)
	{
		int opt=read(),l=read(),r=read();
		if(opt==1) update(1,1,n,l,r);
		else printf("%d\n",query(1,1,n,l,r));
	}
	return 0;
}
/*
3 3
1 2 3
2 1 3
1 1 2
2 2 3
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值