P3747 [六省联考2017]相逢是问候

题意

如果对一个数操作\(k\)次,那么这个数会变成\(c^{c^{...^{a_i}}}\),其中\(c\)\(k\)个。

根据P4139 上帝与集合的正确用法这道题,我们可以知道一个数不断变为自己的欧拉函数,大约\(log\)次就会变成1,而任何数模\(1\)都是\(0\),于是我们可以用势能线段树解决。

因为模数不变,因此我们可以预处理所有\(\varphi(\varphi(...\varphi(p)...))\),之后在线段树上记录操作次数。

这样是三个\(log\)的,因为还要快速幂,可以对每个\(\varphi(\varphi(...\varphi(p)...))\)预处理,用光速幂解决。

注意,扩展中国剩余定理\(a_k\equiv a^{k\%\varphi(p)+\varphi(p)}\pmod{p}\)适用当且仅当\(k\geqslant \varphi(p)\),因此我们在求值时用一个\(flag\)表示是否要\(+\varphi(p)\)

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
const int maxn=50010;
const int maxt=10010;
int n,m,mod,C,maxtim;
int a[maxn];
int pw1[60][maxt],pw2[60][maxt];
bool flag;
bool flag1[60][maxt],flag2[60][maxt];
vector<int>ve;
struct Seg
{
	#define sum(p) (seg[p].sum)
	#define cnt(p) (seg[p].cnt)
	int sum,cnt;
}seg[maxn<<2];
inline int read()
{
	char c=getchar();int res=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
	return res*f;
}
inline int phi(int x)
{
	int res=x,tmp=x;
	for(int i=2;i*i<=tmp;i++)
	{
		if(tmp%i)continue;
		res=res/i*(i-1);
		while(tmp%i==0)tmp/=i;
	}
	if(tmp>1)res=res/tmp*(tmp-1);
	return res;
}
inline void pre_work()
{
	int tmp=mod;
	ve.push_back(mod);
	while(tmp>1)tmp=phi(tmp),ve.push_back(tmp);
	ve.push_back(1);
	for(unsigned int i=0;i<ve.size();i++)
	{
		pw1[i][0]=1;
		for(int j=1;j<=10000;j++)
		{
			pw1[i][j]=pw1[i][j-1]*C;
			if(pw1[i][j]>=ve[i])pw1[i][j]%=ve[i],flag1[i][j]=1;
			flag1[i][j]|=flag1[i][j-1];
		}
	}
	for(unsigned int i=0;i<ve.size();i++)
	{
		pw2[i][0]=1;flag2[i][1]=flag1[i][10000];
		for(int j=1;j<=10000;j++)
		{
			pw2[i][j]=pw2[i][j-1]*pw1[i][10000];
			if(pw2[i][j]>=ve[i])pw2[i][j]%=ve[i],flag2[i][j]=1;
			flag2[i][j]|=flag2[i][j-1];
		}
	}
}
inline void up(int p)
{
	sum(p)=(sum(ls(p))+sum(rs(p)))%mod;
	cnt(p)=min(cnt(ls(p)),cnt(rs(p)));
}
void build(int p,int l,int r)
{
	if(l==r){sum(p)=a[l];return;}
	int mid=(l+r)>>1;
	build(ls(p),l,mid);build(rs(p),mid+1,r);
	up(p);
}
inline int power(int x,int id)
{
	flag=0;
	int res=pw1[id][x%10000]*pw2[id][x/10000];
	if(res>=ve[id])res%=ve[id],flag=1;
	flag|=flag1[id][x%10000]|flag2[id][x/10000];
	return res;
}
int calc(int x,int dep,int k)
{
	flag=0;
	if(dep==k)
	{
		if(x>=ve[dep])flag=1,x%=ve[dep];
		return x;
	}
	int tmp=calc(x,dep+1,k);
	return power(flag?tmp+ve[dep+1]:tmp,dep);
}
void change(int p,int l,int r,int ql,int qr)
{
	if(cnt(p)>=(int)ve.size()-1)return;
	if(l==r)
	{
		cnt(p)++;
		sum(p)=calc(a[l],0,cnt(p));
		return;
	}
	int mid=(l+r)>>1;
	if(ql<=mid)change(ls(p),l,mid,ql,qr);
	if(qr>mid)change(rs(p),mid+1,r,ql,qr);
	up(p);
}
int query(int p,int l,int r,int ql,int qr)
{
	if(l>=ql&&r<=qr)return sum(p);
	int mid=(l+r)>>1,res=0;
	if(ql<=mid)res=(res+query(ls(p),l,mid,ql,qr))%mod;
	if(qr>mid)res=(res+query(rs(p),mid+1,r,ql,qr))%mod;
	return res;
}
signed main()
{
	//freopen("test.in","r",stdin);
	//freopen("test.out","w",stdout); 
	n=read();m=read();mod=read();C=read();
	for(int i=1;i<=n;i++)a[i]=read();
	build(1,1,n);
	pre_work();
	for(int i=1;i<=m;i++)
	{
		int op=read(),l=read(),r=read();
		if(!op)change(1,1,n,l,r);
		else printf("%lld\n",query(1,1,n,l,r)); 
	}
	return 0;
}
posted @ 2019-12-21 22:00  nofind  阅读(299)  评论(0编辑  收藏  举报