BZOJ4869: [Shoi2017]相逢是问候

BZOJ4869: [Shoi2017]相逢是问候

Description

Informatikverbindetdichundmich.
信息将你我连结。B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数。一共有m个操作,可以
分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是
输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为
这个结果可能会很大,所以你只需要输出结果mod p的值即可。

Input

第一行有三个整数n,m,p,c,所有整数含义见问题描述。
接下来一行n个整数,表示a数组的初始值。
接下来m行,每行三个整数,其中第一个整数表示了操作的类型。
如果是0的话,表示这是一个修改操作,操作的参数为l,r。
如果是1的话,表示这是一个询问操作,操作的参数为l,r。
1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p

Output

对于每个询问操作,输出一行,包括一个整数表示答案mod p的值。

Sample Input

4 4 7 2
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3

Sample Output

0
3

题解Here!

首先,有个题建议先做一做:

BZOJ3884: 上帝与集合的正确用法

然后你会发现你已经会了本题的重要思想:

$$c^{c^{c^{...}}}equiv c^{(c^{c^{...}}mod varphi(p)+varphi(p))}(mod p)$$

当然,那个前提还是没变:$c^{c^{...}}>varphi(p)$

然后递归计算即可。

但是问题又出来了,区间操作怎么办?

不怕!我们还有一大堆数据结构没用呢!

当然前提是有个题建议做一做:

BZOJ3211: 花神游历各国

然后我们就可以类似地发现:当指数层数达到一定数量时,$c^{c^{c^{...}}}mod p$的值不再变化。

所以直接线段树暴力单点修改,达到临界条件就打个标记丢一边不管了。

可是,这玩意写完了,我们发现这玩意的复杂度惊人啊:$O(mlog_2nlog_2^2p)$

这个怎么办呢?

不怕,我们拿出终极优化——欧拉函数很少!

这个有什么用?

这意味着模数很少!

所以我们可以考虑将快速幂预处理一下,分成$c^imod p$和$c^{10000 imes i}mod p$两部分。

查询的时候就直接把两块合并就好。

但是!欧拉定理的运用是有限制条件的!

所以我们还需要一个$bool$数组来记录这个条件是否满足。

于是我们省去了一个$log_2p$。

总复杂度$O(mlog_2nlog_2p)$,可以通过此题。

附上奇丑无比的代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define LSON rt<<1
#define RSON rt<<1|1
#define DATA(x) a[x].data
#define SIGN(x) a[x].times
#define LSIDE(x) a[x].l
#define RSIDE(x) a[x].r
#define MAXN 50010
#define MAXM 60
using namespace std;
int n,m,base,mod,min_times=0;
long long val[MAXN],phi[MAXM],pow_one[MAXN/5][MAXM],pow_two[MAXN/5][MAXM];
bool flag,flag_one[MAXN/5][MAXM],flag_two[MAXN/5][MAXM];
struct Segment_Tree{
	long long data;
	int times,l,r;
}a[MAXN<<2];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline long long min(const long long &x,const long long &y){return x<y?x:y;}
long long mexp(long long a,long long b,long long c){
	long long s=1;
	while(b){
		if(b&1)s=s*a%c;
		a=a*a%c;
		b>>=1;
	}
	return s;
}
long long get_phi(long long x){
	long long limit=sqrt(x),s=x;
	for(long long i=2;i<=limit;i++){
		if(x%i==0){
			s=s*(i-1)/i;
			while(x%i==0)x/=i;
		}
	}
	if(x>1)s=s*(x-1)/x;
	return s;
}
void make(){
	int limit=10000;
	long long x=mod;
	phi[0]=mod;
	while(x!=1){
		x=get_phi(x);
		phi[++min_times]=x;
	}
	phi[++min_times]=1;
	for(int i=0;i<=min_times;i++){
		pow_one[0][i]=1;
		for(int j=1;j<=limit;j++){
			pow_one[j][i]=pow_one[j-1][i]*base;
			if(pow_one[j][i]>=phi[i]){
				pow_one[j][i]%=phi[i];
				flag_one[j][i]=true;
			}
			flag_one[j][i]|=flag_one[j-1][i];
		}
	}
	for(int i=0;i<=min_times;i++){
		pow_two[0][i]=1;
		flag_two[1][i]=flag_one[limit][i];
		for(int j=1;j<=limit;j++){
			pow_two[j][i]=pow_two[j-1][i]*pow_one[limit][i];
			if(pow_two[j][i]>=phi[i]){
				pow_two[j][i]%=phi[i];
				flag_two[j][i]=true;
			}
			flag_two[j][i]|=flag_two[j-1][i];
		}
	}
}
inline long long calculate(long long x,long long v){
	flag=false;
	long long p=x%10000,q=x/10000,s=pow_one[p][v]*pow_two[q][v];
	if(s>=phi[v]){
		s%=phi[v];
		flag=true;
	}
	flag|=flag_one[p][v]|flag_two[q][v];
	return s;
}
long long solve(long long x,int deep,int limit){
	flag=false;
	if(deep==limit){
		if(x>=phi[deep]){
			flag=true;
			x%=phi[deep];
		}
		return x;
	}
	long long s=solve(x,deep+1,limit);
	return calculate((flag?(s+phi[deep+1]):s),deep);
}
inline void pushup(int rt){
	DATA(rt)=(DATA(LSON)+DATA(RSON))%mod;
	SIGN(rt)=min(SIGN(LSON),SIGN(RSON));
}
void buildtree(int l,int r,int rt){
	LSIDE(rt)=l;RSIDE(rt)=r;
	if(l==r){
		DATA(rt)=val[l];
		SIGN(rt)=0;
		return;
	}
	int mid=l+r>>1;
	buildtree(l,mid,LSON);
	buildtree(mid+1,r,RSON);
	pushup(rt);
}
void update(int l,int r,int rt){
	if(SIGN(rt)>=min_times)return;
	if(LSIDE(rt)==RSIDE(rt)){
		SIGN(rt)++;
		DATA(rt)=solve(val[LSIDE(rt)],0,SIGN(rt));
		return;
	}
	int mid=LSIDE(rt)+RSIDE(rt)>>1;
	if(l<=mid&&SIGN(LSON)<min_times)update(l,r,LSON);
	if(mid<r&&SIGN(RSON)<min_times)update(l,r,RSON);
	pushup(rt);
}
long long query(int l,int r,int rt){
	long long ans=0;
	if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
	int mid=LSIDE(rt)+RSIDE(rt)>>1;
	if(l<=mid)ans=(ans+query(l,r,LSON))%mod;
	if(mid<r)ans=(ans+query(l,r,RSON))%mod;
	return ans;
}
void work(){
	int f,x,y;
	while(m--){
		f=read();x=read();y=read();
		if(f==0)update(x,y,1);
		else printf("%lld
",query(x,y,1));
	}
}
void init(){
	n=read();m=read();mod=read();base=read();
	for(int i=1;i<=n;i++)val[i]=read();
	buildtree(1,n,1);
	make();
}
int main(){
	init();
	work();
	return 0;
}
原文地址:https://www.cnblogs.com/Yangrui-Blog/p/10385429.html