zoukankan      html  css  js  c++  java
  • 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(mlog_2nlog_2^2p)$

    这个怎么办呢?

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

    这个有什么用?

    这意味着模数很少!

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

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

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

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

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

    总复杂度$O(mlog_2nlog_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
    ",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;
    }
    
  • 相关阅读:
    Spring AOP详解 、 JDK动态代理、CGLib动态代理
    mysql 日期 字符串 时间戳转换
    图文:通过sql server 连接mysql
    c# 数据绑定之 DataFormatString 格式
    sql 截取字符串与 截取字符串最长的字符串
    oracle 清除表空间
    sql 遍历结果print和表格形式
    国家与城市的sql
    sql2005 将一列的多行内容拼接成一行
    oracle和mssql中复制表的比较
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/10385429.html
Copyright © 2011-2022 走看看