博客园 首页 私信博主 显示目录 隐藏目录 管理

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

相逢是问候

2017-09-09


Description

Informatik verbindet dich und mich.
信息将你我连结。(s:看到这个就感觉有毒
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
1 40 19910626 2
0 
0 1 1
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1
0 1 1
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
0 1 1 
1 1 1 
in_put
1 

2 

4 

16 

65536 

11418102

18325590

13700558

13700558

13700558

13700558

13700558

13700558

13700558

13700558

13700558

13700558

13700558

13700558

13700558
out_put

考试时这个题感觉好神奇..当时某个学长讲的时候一脸mengbier,整个人都noip了..什么是φ?

欧拉函数裸题,一个数c在mod一个数的时候,在最多log2p次会不变....

所以线段树维护区间和,区间修改最小值;

修改时遍历到每一个节点,暴力修改每一个改变的值,因为到一定的次数不会变,所以记录一个tag代表这个区间一共修改了多少次...

当l==r时就是那个点修改次数...最多log2p次就不用改了,时间复杂度n*2log(n)。记住,欧拉不要乱mod,会出事的...

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ll (long long)
#define LL long long 
#define IN int 
using namespace std;
const IN maxn=50000+99;
const IN N=10000+99;
int read(){
    int an=0,f=1;
    char ch=getchar();
    while(!('0'<=ch&&ch<='9')){if(ch=='-')f=-f;ch=getchar();}
    while('0'<=ch&&ch<='9'){an=an*10+ch-'0';ch=getchar();}
    return an*f;
}
IN phi[35],a[maxn],F;
IN n,m,p,c;
IN prime[maxn],isp[maxn],k,maxp;//k是质数个数,ips说明这是和数 
struct saber{
IN sum,tag,l,r;
}tr[maxn<<2];
IN Phi(IN x){
    LL ans=x;
    for(IN i=1;i<=k && prime[i]*prime[i]<=x;i++){
        if(!(x%prime[i]))ans=ans*(prime[i]-1)/prime[i];
        while(!(x%prime[i]))x=x/prime[i];
    }
    if(x>1)ans=ans/x*(x-1);
    return ans;
}
void euler(){
    for(IN i=2;i<=N;i++){
        if(!isp[i])k++,prime[k]=i;
        for(IN j=1;j<=k;j++){
            if(i*prime[j]>N)break;
            isp[i*prime[j]]=1;
            if(!(i%prime[j]))break;
        }
    }
    phi[maxp]=p;
    while(phi[maxp]!=1)maxp++,phi[maxp]=Phi(phi[maxp-1]);
    maxp++;
    phi[maxp]=1;
}
void build(IN k,IN l,IN r){
    tr[k].l=l;
    tr[k].r=r;
    if(l==r){
        tr[k].sum=a[l];
        return ;
    }
    IN mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
}
IN ask(IN k,IN i,IN j){
    IN l=tr[k].l,r=tr[k].r;
    if(l==i&&r==j)return tr[k].sum;
    IN mid=(l+r)>>1;
    if(mid>=j)return ask(k<<1,i,j);
    else if(i>mid)return ask(k<<1|1,i,j);
    else return (ask(k<<1,i,mid)+ask(k<<1|1,mid+1,j))%p;
}
IN qw(LL k,IN mod){
    LL ans=1;LL s=c;
    while(k){
        if(k&1)ans*=s;
        k>>=1;
        s*=s;
        if(s>=mod)F=1,s%=mod;
        if(ans>=mod)F=1,ans%=mod;
    }
    return ans%mod;
}
LL work(LL a,LL t){
    LL tmp=a;
    if(tmp>phi[t])tmp=tmp%phi[t]+phi[t];
    for(IN i=t;i>0;i--){
        F=0;tmp=qw(tmp,phi[i-1]);
        if(F)tmp+=phi[i-1],F=0;
    }
    return tmp;
}
void change(IN k,IN i,IN j){
    if(tr[k].tag>=maxp)return;
    LL l=tr[k].l,r=tr[k].r;
    if(l==r){
        tr[k].tag++;
        tr[k].sum=work(a[l],tr[k].tag)%p;
        return;
    }
    IN mid=(l+r)>>1;
    if(mid>=j)change(k<<1,i,j);
    else if(i>mid)change(k<<1|1,i,j);
    else {change(k<<1,i,mid);change(k<<1|1,mid+1,j);}
    tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
    tr[k].tag=min(tr[k<<1].tag,tr[k<<1|1].tag);
}
int main(){
    n=read();m=read();p=read();c=read();
    for(IN i=1;i<=n;i++)a[i]=read();
    euler();
    build(1,1,n);
    while(m)
    {m--;
        IN x,y,z;
        x=read();y=read();z=read();
        if(x){
            printf("%lld\n",ask(1,y,z)%p);
        }
        else {
            change(1,y,z);
        }
    }
    return 0;
}
相逢是问候

by:s_a_b_e_r


表示考试结束学长讲题的时候一脸memgbier+1

欧拉定理+线段树维护

求ccc……a[i] %p的值

看上去一脸不可做,这要乘多少次

然而这个世界上存在着一个神奇的定理——欧拉定理EXT

a≡ax%φ(m)+φ(m)(mod m)

既然我们要求ccx(mod p)

不妨设d=c于是我们要求的就变成了cd

据欧拉定理有cd=cd%φ(p)+φ(p)(mod p)

即ccx=ccx%φ(p)+φ(p)(mod p)

看不清?放大一下

ccx=ccx%φ(p)+φ(p)(mod p)

标记的部分是不是很眼熟?

这部分又满足了欧拉定理

于是再抽一次φ变成

ccx%φ(φ(p))+φ(φ(p))%φ(p)+φ(p)(mod p)

这样一直抽下去,最后要%的东西会变成φ(φ(φ(φ(……p)))) (特别多个φ)

特别玄学的是这东西被φ多了就会变成1(据说最多log(p)次?)

然后就会出现x%1+1 = 0+1 = 1的状况

然后再对它修改就没什么用了,可以无视了

于是一发线段树维护每个点被修改了多少次

以及涉及欧拉的部分都不要乱%p,包括快速幂,容易出事qwq

 

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const int N=50099,SP=10099;
int pri[N],cp,phi[50],ci;
bool isp[N],f;
int n,m,p,c,a[N];
struct tree{
int l,r,tag;
ll sum;
}t[N<<2];
void update(int k){
     t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
     t[k].tag=min(t[k<<1].tag,t[k<<1|1].tag);
}
void build(int k,int l,int r){
     t[k].l=l,t[k].r=r;
     if(l==r){t[k].sum=a[l];return;}
     int mid=(l+r)>>1;
     build(k<<1,l,mid);
     build(k<<1|1,mid+1,r);
     update(k);
}
int Phi(int x){//求欧拉函数
    int ans=x;
    for(int i=1;i<=cp&&pri[i]*pri[i]<=x;++i)
    {
        if(!(x%pri[i]))ans=ans/pri[i]*(pri[i]-1);
        while(!(x%pri[i]))x/=pri[i];
    }
    if(x>1)ans=ans/x*(x-1);
    return ans;
}
void prime(){//欧拉筛素数
     for(int i=2;i<=SP;++i)
     {
         if(!isp[i])pri[++cp]=i;
         for(int j=1;j<=cp;++j)
         {
           if(i*pri[j]>N)break;
           isp[i*pri[j]]=1;
           if(i%pri[j]==0)break;
         }
     }
     phi[ci]=p;
     while(phi[ci]!=1)phi[++ci]=Phi(phi[ci-1]);
     phi[++ci]=1;
}
int kp(int x,int k,int mod){
    int ans=1;
    while(k)
    {
      if(k&1){
          if(1ll*ans*x>=mod)f=1;
          ans=1ll*ans*x%mod;
      }
      if(1ll*x*x>=mod)f=1;
      x=1ll*x*x%mod;
      k>>=1;
    }
    return ans;
}
int C(int a,int x){//抽φ 
    int tmp=a;
    if(tmp>phi[x])tmp=tmp%phi[x]+phi[x];//欧拉定理前提条件 
    for(int i=x;i>0;--i)
    {
      f=0;
      tmp=kp(c,tmp,phi[i-1]);
      if(f==1)tmp+=phi[i-1];
    }
    return tmp;
}
void change(int k,int L,int R){
    if(t[k].tag>=ci)return;
    int l=t[k].l,r=t[k].r;
    if(l==r)
    {
      t[k].sum=C(a[l],++t[k].tag);
      return;
    }
    int mid=(l+r)>>1;
    if(L<=mid)change(k<<1,L,R);
    if(R>mid)change(k<<1|1,L,R);
    update(k);
}
ll query(int k,int L,int R){
    int l=t[k].l,r=t[k].r;
    if(L<=l&&R>=r)return t[k].sum%p;
    if(r<L||l>R)return 0;
    return (query(k<<1,L,R)+query(k<<1|1,L,R))%p;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&p,&c);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    build(1,1,n);
    prime();
    for(int i=1;i<=m;++i)
    {
      int type,l,r;
      scanf("%d%d%d",&type,&l,&r);
      if(!type)change(1,l,r);
      else printf("%lld\n",query(1,l,r)%p);
    }
    return 0;
}
verbinden

 

by:wypx


 s:省选后听见有人说谁mo谁是真***,今天我就在ans膜了个p,╮(╯▽╰)╭

w:这说明了什么(笑)

 

s:%数学dalao

w:果然数学题还是要放⑨啊w

posted @ 2017-09-09 20:20  ck666  阅读(1045)  评论(0编辑  收藏  举报