【SHOI2017】相逢是问候【扩展欧拉定理】【复杂度分析】【线段树】

题意:给定一个长度为 n n n 的序列 a i a_i ai,维护 m m m 次操作:

  1. 区间执行 a i ← c a i a_i \leftarrow c^{a_i} aicai
  2. 区间求和 模 p p p

其中 p , c p,c p,c 对所有操作相同。

n , m ≤ 5 × 1 0 4 , p ≤ 1 0 8 n,m\leq 5\times 10^4,p\leq 10^8 n,m5×104,p108

不建议写,真的浪费生命

根据扩展欧拉定理,显然修改 O ( log ⁡ p ) O(\log p) O(logp) 次就没用了,所有数总共只会修改 O ( n log ⁡ P ) O(n\log P) O(nlogP) 次。

但是暴力的话循环是占复杂度的,我们需要快速判断某个区间是否还需要修改。

似乎就只能线段树了?

暴力预处理出每个数 φ   60 \varphi \text{ } 60 φ 60 次内的结果,然后线段树维护区间修改次数最小值和当前的区间和。修改的时候如果当前区间最小修改次数达到 60 60 60 就退出,否则暴力递归。

复杂度 O ( n log ⁡ 2 P ) O(n\log^2 P) O(nlog2P) ,会被卡,根号预处理快速幂可以 O ( n log ⁡ P ) O(n\log P) O(nlogP)

听上去还挺美好,然而这题最恶心的地方在判断扩展欧拉定理的适用条件……

方法是在快速幂(或预处理幂)的时候记录是否取过模。

Q:如何证明不会出现 x > φ ( p ) , c x % φ ( p ) + φ ( p ) < p x>\varphi (p),c^{x \%\varphi(p)+\varphi(p)}<p x>φ(p),cx%φ(p)+φ(p)<p ,然后继续迭代的时候崩掉的情况?

A:实际上 c φ ( p ) ≥ p c^{\varphi(p)} \geq p cφ(p)p 一般都是对的。

考虑计算 φ ( p ) \varphi(p) φ(p) 的一个很松的下界: φ ( p ) \varphi(p) φ(p) 考虑了每个质因子 x x x ,然后 p ← p × x − 1 x p\leftarrow p\times \dfrac {x-1}x pp×xx1

根据糖水原理,这个 x x x 越小效果会越明显。而 x x x 小的时候能得到的质因子也会更多。

1 0 8 10^8 108 以内的数最多只有 8 8 8 个质因子,所以可以估算一个下界 φ ( p ) p ≥ 1 2 × 2 3 × 4 5 × ⋯ × 18 19 ≈ 0.17 \dfrac{\varphi(p)}{p}\geq\dfrac 12 \times \dfrac 23\times \dfrac 45\times \dots\times \dfrac {18}{19} \approx 0.17 pφ(p)21×32×54××19180.17

相当于要证明: 2 p / 7 ≥ p 2^{p/7}\geq p 2p/7p ,前者增长远快于后者, p p p 足够大(也就几十)的时候该式成立。

小数据暴力打表知只有个 p = 6 p=6 p=6 ,但 6 6 6 只能取 2 2 2 φ \varphi φ ,所以也崩不了

证毕

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <bitset>
#include <utility>
#define MAXN 50005
#define re register
using namespace std;
typedef long long ll;
int n,q,c,flag;
inline int mul(const int& a,const int& b,const int& MOD)
{
	ll c=(ll)a*b;
	if (c>=MOD) flag=1;
	return c%MOD;
}
int m[60],len;
typedef pair<int,int> pi;
const int N=2e4;
pi p1[60][N+5],p2[60][N+5];//i,i*10000
inline void init()
{
	for (int T=0;T<=len;T++)
	{
		p1[T][0]=p2[T][0]=make_pair(1,0);
		flag=0;
		for (int i=1;i<N;i++) p1[T][i].first=mul(p1[T][i-1].first,c,m[T]),p1[T][i].second=flag;
		p2[T][1].first=mul(p1[T][N-1].first,c,m[T]),p2[T][1].second=flag;
		for (int i=2;i<=N;i++) p2[T][i].first=mul(p2[T][i-1].first,p2[T][1].first,m[T]),p2[T][i].second=flag;
	}
}
inline int qpow(int p,int i)
{
	pi x=p1[i][p%N],y=p2[i][p/N];
	flag|=(x.second|y.second);
	return mul(x.first,y.first,m[i]);
}
inline int calcphi(int x)
{
	int ans=1;
	for (int i=2;i*i<=x;i++)
		if (x%i==0)
		{
			ans*=i-1,x/=i;
			while (x%i==0) ans*=i,x/=i;
		}
	if (x>1) ans*=x-1;
	return ans;
}
int a[MAXN][60];
#define lc p<<1
#define rc p<<1|1
int sum[MAXN<<2],mcnt[MAXN<<2];
inline void update(int p){mcnt[p]=min(mcnt[lc],mcnt[rc]),sum[p]=(sum[lc]+sum[rc])%m[0];}
void build(int p,int l,int r)
{
	if (l==r)
	{
		scanf("%d",&a[l][0]);
		for (int i=1;i<=len;i++)
		{
			flag=0;
			int x=a[l][0];
			for (int j=i;j>=1;j--) x=qpow(x+m[j]*flag,j-1);
			a[l][i]=x;
		}
		sum[p]=a[l][0];
		return;
	}
	int mid=(l+r)>>1;
	build(lc,l,mid);build(rc,mid+1,r);
	update(p);
}
void modify(int p,int l,int r,int ql,int qr)
{
	if (qr<l||r<ql) return;
	if (mcnt[p]>=len) return;
	if (l==r) return (void)(sum[p]=a[l][++mcnt[p]]);
	int mid=(l+r)>>1;
	modify(lc,l,mid,ql,qr),modify(rc,mid+1,r,ql,qr);
	update(p);
}
int query(int p,int l,int r,int ql,int qr)
{
	if (qr<l||r<ql) return 0;
	if (ql<=l&&r<=qr) return sum[p];
	int mid=(l+r)>>1;
	return (query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr))%m[0];
}
int main()
{
	scanf("%d%d%d%d",&n,&q,&m[0],&c);
	for (len=1;(m[len]=calcphi(m[len-1]))>1;++len);
	m[++len]=1;
	init();
	build(1,1,n);
	while (q--)
	{
		int t,l,r;
		scanf("%d%d%d",&t,&l,&r);
		if (t==0) modify(1,1,n,l,r);
		else printf("%d\n",query(1,1,n,l,r));
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值