zoukankan      html  css  js  c++  java
  • SHOI 2017 相逢是问候(扩展欧拉定理+线段树)

    题意

    https://loj.ac/problem/2142

    思路

    一个数如果要作为指数,那么它不能直接对模数取模,这是常识;

    诸如 (c^{c^{c^{c..}}}) 的函数递增飞快,不是高精度可以描述的,这也是常识。

    所以,此题要用到很多数论知识。

    欧拉函数

    定义

    (varphi(n))([1,n]) 中与 (n) 互质的正整数个数(包括 (1))。

    通式

    (displaystyle varphi(n)=nprod_{p|n}(1-{1over p})) 其中 (p)(x) 的质因子。

    如何理解这个式子呢?可以粗略这样理解:(displaystyle (1-{1over p})) 意思就是筛掉所有能被 (p) 整除的数,当然这种理解方法是错误的,只能算是感性理解,方便记忆。

    前几项

    摘自维基百科:
    欧拉函数前143项

    性质

    几条比较重要的性质:
    (gcd(m,n)=1),有 (varphi(mn)=varphi(m)varphi(n))
    (m=2),不难得出当 (n) 为奇数时,(varphi(2n)=varphi(n))
    另外,对于质数 (p),有 (varphi(p)=p-1)

    欧拉定理

    (a^{varphi(n)} equiv 1 pmod n) 其中 (gcd(a,n)=1)

    (n) 为质数的情况就是著名的费马小定理

    扩展欧拉定理

    [a^b=egin{cases} a^bquad quad quad quadquad quad b<varphi(p)\ a^{bmodvarphi(p)+varphi(p)}quad bgeq varphi(p) end{cases}pmod p ]

    扩展欧拉定理把幂的层数和模数都降了下来,用它就可以解决本题了。

    回归本题

    先考虑暴力做法再尝试优化。我们可以给每个数一个标记,表示多少层 (c) 上才是 (a_i)。然后每次只需计算这个 (c^{c^{c^{...^{a_i}}}}) 即可。

    套用扩展欧拉定理,假设标记为 (3),就是计算(c^{c^{c^{a_i}}} mod p) 的值。

    分两类讨论,当 (c^{c^{a_i}}<varphi(p)),问题就转化成了求 (c^{c^{a_i}}mod varphi(p)) 的值;否则,只需在此基础上加 (varphi(p)) 即可,而判断大小只需在快速幂上传一个标记就行了。
    一路递归下去,当模数变成 (1) 时直接返回 (0) 即可。

    考虑优化。由于模数给定,那么变化显然是从 (p)(varphi(p)) 再到 (varphi(varphi(p))) 这样一路变化的。变化不会超过 (log p) 层,那么总标记次数就不超过 (nlog p) 次。

    可以用线段树维护一个区间,那么再修改一个区间时,也维护一个区间的最小修改次数的最大值,当它对应的模数已经是 (1) 时,便不进行修改。再修改时,要算出 (c^{c^{c^{...^{a_i}}}}) 的值,最多修改 (nlog n) 次,最深又有 (log) 层,算快速幂又有一个 (log)。三个 (log) 是过不了本题的。

    然而快速幂可以直接分块预处理,由于模数不超过 (log n) 个,则分别预处理
    (c^{i} mod p_k,c^{50000i} mod p_k) 的值,以及和 (p_k) 的大小关系即可。复杂度降至两个 (log)

    查询直接区间求和,一个 (log)

    代码

    #include<bits/stdc++.h>
    #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
    #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
    typedef long long LL;
    using namespace std;
    const int N=5e4+5;
    const int _N=5e4;
    int P[105],a[N],tot,n,m,c;
    LL PW[2][N][105];bool FL[2][N][105];
    //PW[i][j][k]  c^(i*_N+j)%P[k]
    //FL[i][j][k] [c^(i*_N+j)>=P[k]]
    LL phi(LL n)
    {
    	LL res=n;
    	for(LL i=2;i*i<=n;i++)if(n%i==0)
    	{
    		res=res/i*(i-1);
    		while(n%i==0)n/=i;
    	}
    	if(n>1)res=res/n*(n-1);
    	return res;
    }
    struct node
    {
    	int Mi,sum;
    	node operator +(const node &_)const
    	{
    		return (node){min(Mi,_.Mi),(sum+_.sum)%P[0]};
    	}
    }nd[N<<2];
    void build(int k,int l,int r,int *arr)
    {
    	if(l==r)
    	{
    		nd[k].Mi=0;
    		nd[k].sum=arr[l];
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(k<<1,l,mid,arr);
    	build(k<<1|1,mid+1,r,arr);
    	nd[k]=nd[k<<1]+nd[k<<1|1];
    }
    LL Pow(LL p,int id,bool &flag)//c^p%P[id] flag=[c^p>=P[id]]
    {
    	flag=FL[0][p%_N][id]||FL[1][p/_N][id]||(PW[0][p%_N][id]*PW[1][p/_N][id]>=P[id]);
    	return PW[0][p%_N][id]*PW[1][p/_N][id]%P[id];
    }
    LL calc(int x,LL y)
    {
    	if(y>=P[x])y=y%P[x]+P[x];
    	DOR(i,x,1)
    	{
    		bool flag;
    		y=Pow(y,i-1,flag);
    		if(flag)y+=P[i-1];
    	}
    	return y%P[0];
    }
    void update(int k,int L,int R,int l,int r)
    {
    	if(L<=l&&r<=R&&nd[k].Mi>=tot)return;
    	if(l==r)
    	{
    		nd[k].sum=calc(++nd[k].Mi,a[l]);
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(L<=mid)update(k<<1,L,R,l,mid);
    	if(R>mid)update(k<<1|1,L,R,mid+1,r);
    	nd[k]=nd[k<<1]+nd[k<<1|1];
    }
    int query(int k,int L,int R,int l,int r)
    {
    	if(L<=l&&r<=R)return nd[k].sum;
    	int mid=(l+r)>>1;
    	if(R<=mid)return query(k<<1,L,R,l,mid);
    	else if(L>mid)return query(k<<1|1,L,R,mid+1,r);
    	else return (query(k<<1,L,R,l,mid)+query(k<<1|1,L,R,mid+1,r))%P[0];
    }
    void pre_compute()
    {
    	FOR(i,0,tot)
    	{
    		PW[0][0][i]=1,FL[0][0][i]=(1>=P[i]);
    		FOR(j,1,_N)
    		{
    			PW[0][j][i]=PW[0][j-1][i]*c;
    			FL[0][j][i]=FL[0][j-1][i]|(PW[0][j][i]>=P[i]);
    			if(PW[0][j][i]>=P[i])PW[0][j][i]%=P[i];
    		}
    		
    		PW[1][0][i]=1,FL[1][0][i]=(1>=P[i]);
    		FOR(j,1,_N)
    		{
    			PW[1][j][i]=PW[1][j-1][i]*PW[0][_N][i];
    			FL[1][j][i]=FL[1][j-1][i]|(PW[1][j][i]>=P[i]);
    			if(PW[1][j][i]>=P[i])PW[1][j][i]%=P[i];
    		}
    	}
    }
    
    int main()
    {
    	scanf("%d%d%d%d",&n,&m,&P[tot=0],&c);
    	FOR(i,1,n)scanf("%d",&a[i]);
    	while(P[tot]!=1)
    	{
    		P[tot+1]=phi(P[tot]);
    		tot++;
    	}
    	P[++tot]=1;
    	pre_compute();
    	build(1,1,n,a);
    	while(m--)
    	{
    		int op,l,r;
    		scanf("%d%d%d",&op,&l,&r);
    		if(op==0)update(1,l,r,1,n);
    		else printf("%d
    ",query(1,l,r,1,n));
    	}
        return 0;
    }
    
  • 相关阅读:
    python实现从文件夹随机拷贝出指定数量文件到目标文件夹
    keras训练函数fit和fit_generator对比,图像生成器ImageDataGenerator数据增强
    Tensorflow与Keras自适应使用显存
    python创建DataFrame,并实现DataFrame的转置
    git push和pull如何解决冲突!!!精品
    lsomap降维
    python变量拷贝
    TSNE-图像二分类可视化
    用清华源安装模块
    HDU多校Round 8
  • 原文地址:https://www.cnblogs.com/Paulliant/p/10197306.html
Copyright © 2011-2022 走看看