bzoj 4869: [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

HINT

鸣谢多名网友提供正确数据,已重测!

思路: 这个题目也是扩展欧拉定理的应用,只不过和线段树结合起来了。

根据扩展欧拉定理的性质,我们知道题目的操作经过几次以后就会让每个ai变成定值(不超过log次),所以我们做区间修改的时候只要暴力修改就可以了。

也就是我们最多只要修改nlogn次,每次修改的时候都要用扩展欧拉定理重新求一下值,由于要涉及到快速幂,所以每次重新求值就需要log*log

这样总的时间复制度就是n*log^3。 比较容易被卡。

我们可以对幂次做预处理,这样时间复杂度可以优化到n*log^2,这样就很快了。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define LL long long
  4 #define lc (x<<1)
  5 #define rc (x<<1|1)
  6 #define mid (l+r)/2
  7 int const N=50000+10;
  8 int c[40][10001],cc[40][10001],p,n,m,C,a[N],s[N<<2],sum[N<<2],phi[40],num;
  9 int ph(int x){
 10     int ret=x;
 11     for(int i=2;i*i<=x;i++)
 12         if(x%i==0){
 13             ret=ret/i*(i-1);
 14             while (x%i==0) x/=i;
 15         }
 16     if(x>1) ret=ret/x*(x-1);
 17     return ret;
 18 }
 19 void build(int x,int l,int r){
 20     if(l==r){
 21         sum[x]=a[l]%phi[0];  return;
 22     }
 23     build(lc,l,mid);
 24     build(rc,mid+1,r);
 25     sum[x]=(sum[lc]+sum[rc])%phi[0];
 26 }
 27
 28 int calc(unsigned long long  x,int p){
 29     int check=0;
 30     int tx=x,tp=p;
 31 //    if(x>=phi[p+1]) x=x%phi[p+1]+phi[p+1];
 32     while (p>-1){
 33         if(check){
 34             x=x%phi[p+1]+phi[p+1];
 35             int num1=x/10000,num2=x%10000;
 36             x=1LL*c[p][num2]*cc[p][num1]%phi[p];
 37         }else {
 38             unsigned long long tmp=1;
 39             if(C!=1)
 40             for(int i=1;i<=x;i++){
 41                 tmp=tmp*C;
 42                 if(tmp>=phi[p]) {
 43                     check=1;
 44                     int num1=x/10000,num2=x%10000;
 45                     tmp=1LL*c[p][num2]*cc[p][num1]%phi[p];
 46                     break;
 47                 }
 48             }
 49             x=tmp;
 50         }
 51         p--;
 52     }
 53     //if(tp>=1) cout<<tx<<" **** "<<tp<<" "<<x<<endl;
 54     return x;
 55 }
 56 void insert(int x,int l,int r,int ll,int rr){
 57     if(s[x]>num) return;
 58     if(l==r){
 59         s[x]++;
 60         sum[x]=calc(a[l],s[x]-1);
 61         return;
 62     }
 63     if(ll<=mid) insert(lc,l,mid,ll,rr);
 64     if(rr>mid)  insert(rc,mid+1,r,ll,rr);
 65     s[x]=min(s[lc],s[rc]);
 66     sum[x]=(sum[lc]+sum[rc])%phi[0];
 67 }
 68 int query(int x,int l,int r,int ll,int rr){
 69     if(ll<=l && r<=rr) return sum[x];
 70     int ret=0;
 71     if(ll<=mid) ret+=query(lc,l,mid,ll,rr);
 72     if(rr>mid)  ret+=query(rc,mid+1,r,ll,rr);
 73     return ret%phi[0];
 74 }
 75 int main(){
 76     //freopen("9.in","r",stdin);
 77     scanf("%d%d%d%d",&n,&m,&p,&C);
 78     phi[0]=p;
 79     while (1){
 80         c[num][0]=1;
 81         unsigned long long tc=C;
 82         for(int i=1;i<=10000;i++) c[num][i]=1LL*c[num][i-1]*C%p;
 83         cc[num][0]=1;
 84         for(int i=1;i<=10000;i++) cc[num][i]=1LL*cc[num][i-1]*c[num][10000]%p;
 85         if(p==1) break;
 86         phi[++num]=p=ph(p);
 87     }
 88     phi[++num]=1;
 89     //cout<<num<<" "<<phi[0]<<" "<<phi[1]<<endl;
 90     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
 91     build(1,1,n);
 92     while (m--){
 93         int opt,l,r;
 94         scanf("%d%d%d",&opt,&l,&r);
 95         if(opt==0)insert(1,1,n,l,r);
 96         else printf("%d\n",query(1,1,n,l,r));
 97         //if(opt==0) break;
 98
 99     }
100     return 0;
101 }

原文地址:https://www.cnblogs.com/ZJXXCN/p/11505015.html

时间: 2024-10-10 11:16:26

bzoj 4869: [Shoi2017]相逢是问候的相关文章

[BZOJ 4869][SXOI2017]相逢是问候(扩展欧拉定理+线段树)

Description Informatik verbindet dich und mich. 信息将你我连结.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因为 这个结果可能会很大,所以你只需

bzoj4869 [Shoi2017]相逢是问候

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4869 [题解] 发现好像没有办法普通维护. 给小盆友们江数论的时候江过x^m mod p = x^(m mod phi(p)) mod p 发现一个数进行phi操作最多log次. 暴力就行啦qwq 注意就是phi的那个最后要补一个phi[++pn] = 1. 为什么呢?因为phi(1) = 1(展开到最后,还要多展开一层(!)) 那么就行啦! 复杂度不大会分析qwq 照理性分析可能是log

【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因为 这个结果可能会很大,所以你只需要输出结

[Shoi2017]相逢是问候

传送门 这种“暴力线段树”可以考虑一下是不是有什么特殊性质. 对于这道题什么要知道: 可能这里写得比较清楚(摘自) 感觉就是一个迭代的过程. 而一个数在操作k次之后就可以不用再操作了. 然后使用欧拉定理的时候要特判一下: x>=phi时,最后要加一个phi 小于则不加. 预处理出所有的phi,记得最后一个phi为1. #include<bits/stdc++.h> #define LL long long #define N 50003 using namespace std; int

[BZOJ4869][六省联考2017]相逢是问候(线段树+扩展欧拉定理)

4869: [Shoi2017]相逢是问候 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1313  Solved: 471[Submit][Status][Discuss] Description Informatikverbindetdichundmich. 信息将你我连结.B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以 分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每

【BZOJ4869】相逢是问候(线段树,欧拉定理)

[BZOJ4869]相逢是问候(线段树,欧拉定理) 题面 BZOJ 题解 根据欧拉定理递归计算(类似上帝与集合的正确用法) 所以我们可以用线段树维护区间最少的被更新的多少次 如果超过了\(\varphi\)的限制 就不用再计算了 如果需要计算就每次暴力算 这样的复杂度\(O(nlog^2)\) #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<

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

相逢是问候 2017-09-09 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因为这个结果可

【BZOJ4869】相逢是问候 [线段树]

相逢是问候 Time Limit: 40 Sec  Memory Limit: 512 MB[Submit][Status][Discuss] Description Informatikverbindetdichundmich. 信息将你我连结.B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以 分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是 输入的一个常数,也就是执行赋值

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

传送门 题解 扩展欧拉定理. 线段树维护,已经全改到底了的节点就不管,不然暴力修改下去. //Achen #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<queue> #include<cmath> const int N=5