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


    题意

    如果对一个数操作(k)次,那么这个数会变成(c^{c^{...^{a_i}}}),其中(c)(k)个。

    根据P4139 上帝与集合的正确用法这道题,我们可以知道一个数不断变为自己的欧拉函数,大约(log)次就会变成1,而任何数模(1)都是(0),于是我们可以用势能线段树解决。

    因为模数不变,因此我们可以预处理所有(varphi(varphi(...varphi(p)...))),之后在线段树上记录操作次数。

    这样是三个(log)的,因为还要快速幂,可以对每个(varphi(varphi(...varphi(p)...)))预处理,用光速幂解决。

    注意,扩展中国剩余定理(a_kequiv a^{k\%varphi(p)+varphi(p)}pmod{p})适用当且仅当(kgeqslant varphi(p)),因此我们在求值时用一个(flag)表示是否要(+varphi(p))

    code:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define ls(p) (p<<1)
    #define rs(p) (p<<1|1)
    const int maxn=50010;
    const int maxt=10010;
    int n,m,mod,C,maxtim;
    int a[maxn];
    int pw1[60][maxt],pw2[60][maxt];
    bool flag;
    bool flag1[60][maxt],flag2[60][maxt];
    vector<int>ve;
    struct Seg
    {
    	#define sum(p) (seg[p].sum)
    	#define cnt(p) (seg[p].cnt)
    	int sum,cnt;
    }seg[maxn<<2];
    inline int read()
    {
    	char c=getchar();int res=0,f=1;
    	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
    	return res*f;
    }
    inline int phi(int x)
    {
    	int res=x,tmp=x;
    	for(int i=2;i*i<=tmp;i++)
    	{
    		if(tmp%i)continue;
    		res=res/i*(i-1);
    		while(tmp%i==0)tmp/=i;
    	}
    	if(tmp>1)res=res/tmp*(tmp-1);
    	return res;
    }
    inline void pre_work()
    {
    	int tmp=mod;
    	ve.push_back(mod);
    	while(tmp>1)tmp=phi(tmp),ve.push_back(tmp);
    	ve.push_back(1);
    	for(unsigned int i=0;i<ve.size();i++)
    	{
    		pw1[i][0]=1;
    		for(int j=1;j<=10000;j++)
    		{
    			pw1[i][j]=pw1[i][j-1]*C;
    			if(pw1[i][j]>=ve[i])pw1[i][j]%=ve[i],flag1[i][j]=1;
    			flag1[i][j]|=flag1[i][j-1];
    		}
    	}
    	for(unsigned int i=0;i<ve.size();i++)
    	{
    		pw2[i][0]=1;flag2[i][1]=flag1[i][10000];
    		for(int j=1;j<=10000;j++)
    		{
    			pw2[i][j]=pw2[i][j-1]*pw1[i][10000];
    			if(pw2[i][j]>=ve[i])pw2[i][j]%=ve[i],flag2[i][j]=1;
    			flag2[i][j]|=flag2[i][j-1];
    		}
    	}
    }
    inline void up(int p)
    {
    	sum(p)=(sum(ls(p))+sum(rs(p)))%mod;
    	cnt(p)=min(cnt(ls(p)),cnt(rs(p)));
    }
    void build(int p,int l,int r)
    {
    	if(l==r){sum(p)=a[l];return;}
    	int mid=(l+r)>>1;
    	build(ls(p),l,mid);build(rs(p),mid+1,r);
    	up(p);
    }
    inline int power(int x,int id)
    {
    	flag=0;
    	int res=pw1[id][x%10000]*pw2[id][x/10000];
    	if(res>=ve[id])res%=ve[id],flag=1;
    	flag|=flag1[id][x%10000]|flag2[id][x/10000];
    	return res;
    }
    int calc(int x,int dep,int k)
    {
    	flag=0;
    	if(dep==k)
    	{
    		if(x>=ve[dep])flag=1,x%=ve[dep];
    		return x;
    	}
    	int tmp=calc(x,dep+1,k);
    	return power(flag?tmp+ve[dep+1]:tmp,dep);
    }
    void change(int p,int l,int r,int ql,int qr)
    {
    	if(cnt(p)>=(int)ve.size()-1)return;
    	if(l==r)
    	{
    		cnt(p)++;
    		sum(p)=calc(a[l],0,cnt(p));
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(ql<=mid)change(ls(p),l,mid,ql,qr);
    	if(qr>mid)change(rs(p),mid+1,r,ql,qr);
    	up(p);
    }
    int query(int p,int l,int r,int ql,int qr)
    {
    	if(l>=ql&&r<=qr)return sum(p);
    	int mid=(l+r)>>1,res=0;
    	if(ql<=mid)res=(res+query(ls(p),l,mid,ql,qr))%mod;
    	if(qr>mid)res=(res+query(rs(p),mid+1,r,ql,qr))%mod;
    	return res;
    }
    signed main()
    {
    	//freopen("test.in","r",stdin);
    	//freopen("test.out","w",stdout); 
    	n=read();m=read();mod=read();C=read();
    	for(int i=1;i<=n;i++)a[i]=read();
    	build(1,1,n);
    	pre_work();
    	for(int i=1;i<=m;i++)
    	{
    		int op=read(),l=read(),r=read();
    		if(!op)change(1,1,n,l,r);
    		else printf("%lld
    ",query(1,1,n,l,r)); 
    	}
    	return 0;
    }
    
  • 相关阅读:
    判断
    迭代器
    如何关闭弹框以外的区域来关闭弹框?
    如何使APP端的滑动事件兼容PC端?
    获取元素相对于视窗的位置?
    如何获取一个元素没有在style和样式表中设置的样式的值?
    如何寻找一个数值数组中的最大元素?
    table表格中字母和数字如何换行?
    js中将字符串作为函数名来调用的方法
    web开发中如何使用引用字体
  • 原文地址:https://www.cnblogs.com/nofind/p/12078423.html
Copyright © 2020-2023  润新知