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

一、题目

点此看题

二、解法

我自己把这道题想出来了哈哈哈(但是考试时候却写锅了嘤嘤嘤

看到我们要求的算式不难联想到欧拉降幂吧,我们先来复习一下欧拉降幂:
a b = { a b m o d    φ ( p ) + φ ( p ) b > φ ( p ) a b o t h e r w i s e m o d    p a^b=\begin{cases}a^{b\mod\varphi(p)+\varphi(p)}&b>\varphi(p)\\a^b&otherwise\end{cases}\mod p ab={abmodφ(p)+φ(p)abb>φ(p)otherwisemodp具体实现中我们没上升一个幂的位次就要将 p p p变成 φ ( p ) \varphi(p) φ(p),当模数是 1 1 1的时候就特别好算了,而 φ \varphi φ的下降到 1 1 1 log ⁡ \log log级别的,所以也能推出当算式“高度”满足了让模数下降到 1 1 1时,这个算式的值就不会再改变了,每个位置就只会被修改 log ⁡ \log log次,而每次修改(直接欧拉降幂)是 log ⁡ 2 \log^2 log2的,所以这样做的总时间复杂度是 O ( n log ⁡ 3 n ) O(n\log ^3n) O(nlog3n)

讲一下具体实现的细节,用并查集优化我们的暴力修改,如果一个位置的算式已经不会改变了,那我们把这个位置的并查集并到下一个位置, i + 1 i+1 i+1时找到他的祖宗,那么就可以达到加速访问的效果了。第二个点就是我们要预处理可能用到的 φ \varphi φ。第三个点就是用通过预处理使得我们能 O ( 1 ) O(1) O(1) c b = c b % 10000 × c 10000 ( b / 10000 ) c^b=c^{b\%10000}\times c^{10000(b/10000)} cb=cb%10000×c10000(b/10000),这样就可以省掉一个 log ⁡ n \log n logn

最后讲一下我考试时候自己写遇到的 4 4 4个锅:

  • 1 1 1:判断一个数是否不会改变的时候我的 k k k(多少次降到 1 1 1)错打成了 m m m(询问组数)
  • 2 2 2:输出答案的时候没有处理好模数,养成习惯,要先模再加再模。
  • 3 3 3,注意 φ = 1 \varphi=1 φ=1的时候还要判断被模数,如果不等于 0 0 0返回 1 1 1,否则返回 0 0 0(严格遵循公式)
  • 4 4 4,注意判断一个算式不会改变应该到 k + 1 k+1 k+1,因为要保证每个模数都对应的是 c c c(我是从 0 0 0开始存的, p h i [ 0 ] = p phi[0]=p phi[0]=p,所以同一个地方出了两个锅)

最后贴上代码,作者懒,写的 O ( n log ⁡ 3 n ) O(n\log^3 n) O(nlog3n) l o j loj loj上过了,但是改起来应该不麻烦

#include <cstdio>
const int M = 50005;
#define int long long
int read()
{
    int num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
int n,m,k,p,c,a[M],b[M],val[M],s[M],ph[M],fa[M];
int find(int x)
{
	if(x!=fa[x]) fa[x]=find(fa[x]);
	return fa[x];
}
int phi(int n)
{
	int r=n;
	for(int i=2;i*i<=n;i++)
		if(n%i==0)
		{
			r=r/i*(i-1);
			while(n%i==0) n/=i;
		}
	if(n>1) r=r/n*(n-1);
	return r;
}
void init()
{
	ph[0]=p;
	for(int i=1;;i++)
	{
		ph[i]=phi(ph[i-1]);
		if(ph[i]==1)
		{
			k=i;
			break;
		}
	}
	for(int i=1;i<=n+1;i++)
		fa[i]=i;
}
int lowbit(int x)
{
	return x&(-x);
}
void upd(int x,int f)
{
	for(;x<=n;x+=lowbit(x))
		b[x]=(b[x]+f)%p;
}
int ask(int x)
{
	int r=0;
	for(;x>=1;x-=lowbit(x))
		r=(r+b[x])%p;
	return r;
}
int qkpow(int a,int b,int p)
{
	int r=1;
	while(b>0)
	{
		if(b&1)
		{
			r=r*a;
			if(r>p) r=r%p+p;
		}
		a=a*a;
		if(a>p) a=a%p+p;
		b>>=1;
	}
	return r;
}
int work(int x,int p)//哪个下标,第几层 
{
	if(p==k)
	{
		if(p==s[x])
		{
			if(a[x]==0) return 0;
			return 1;
		}
		if(c==0) return 0;
		return 1;//模1 锅3 
	}
	if(p==s[x]) return a[x];//到了a[x]
	return qkpow(c,work(x,p+1),ph[p]);
}
signed main()
{
	n=read();m=read();p=read();c=read();
	init();
	for(int i=1;i<=n;i++)
	{
		a[i]=val[i]=read();
		upd(i,a[i]);
	}
	while(m--)
	{
		int op=read(),l=read(),r=read();
		if(op==0)
		{
			for(int i=find(l);i<=r;i=find(i+1))
			{
				upd(i,-val[i]);
				s[i]++;//有多少个c 
				val[i]=work(i,0);
				upd(i,val[i]);
				if(s[i]==k+1) fa[i]=find(i+1);//锅1、锅4
			}
		}
		else
		{
			printf("%lld\n",((ask(r)-ask(l-1))%p+p)%p);//锅2
		}
	}
}
/*
下面是我考试时候写的注释:

感觉我会了
利用phi快速下降的性质!
真是数学考试 哈哈哈
时间复杂度O(nlog^3 n)
所以写一个欧拉降幂+树状数组+并查集 
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值