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(m\log_2n\log_2^2p)$

这个怎么办呢?

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

这个有什么用?

这意味着模数很少!

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

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

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

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

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

总复杂度$O(m\log_2n\log_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\n",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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值