【BZOJ 4869】【2017六省联考】相逢是问候

来源:互联网 发布:mac删除键 编辑:程序博客网 时间:2024/05/17 06:34

考虑一个欧拉定理的扩展:
x>φ(p)时有:
cxcx%φ(p)+φ(p)(modp)
也叫作欧拉定理EXT,证明看这里:https://zhuanlan.zhihu.com/p/24902174
这个公式的强大之处在于a和p可以不互质,也就是说p可以不为质数。
然后证明一个结论:这个操作在操作logP次之后一定是一个定值。
显然可以发现一个数P在最多取O(logP)次欧拉函数之后会变成1,(一个简单的证明:奇数变偶数)。
然后就可以发现在不断地套用欧拉定理的时候,指数模φ(p),指数的指数模φ(φ(p))…………最多O(logP)次之后变成模1加1,也就是1,之后不管有多少指数都会变成1,所以一个数在操作O(logP)次之后再操作就没有意义了。(和http://uoj.ac/problem/228很像,都是操作一定次数之后不变)
考虑使用线段树来做,如果当前区间全部已经不再变化就退出,否则暴力修改下去。所以最坏情况下每个数会修改O(logP)次,每次修改会修改树上O(logN)个节点,所以时间复杂度是O(NlogNlogP)
但是在这里我们默认了修改一次的时间是O(1),然而事实并非如此。对于一个数的快速幂,需要递归O(logP)层(因为每层指数的模数都是不一样的,都要重新算),所以一个数快速幂就要两个log,一共有nlogP个数(对不是n个数,xcxccx这些都要重新算的,我开始就是这里少算了一个log),所以快速幂的预处理就变成了三个log!虽然开了O2,但是如果常数爆炸的话很有可能被卡掉。
接着我们尝试优化掉一个log:计算快速幂的那个log。为什么可以优化掉呢?因为底数一定是c,而指数最大是1e8,那么我们可以预处理cxtx,其中t=c10000x<=10000,对于任何一个幂,指数大于10000的部分从tx的表中找,小于10000的部分从cx的表中找,这样总的时间复杂度降到两个log。

#include<cmath>#include<cstdio>#include<vector>#include<queue>#include<cstring>#include<iomanip>#include<stdlib.h>#include<iostream>#include<algorithm>#define ll long long#define inf 1000000000#define N 50005#define ls x<<1#define rs x<<1|1#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;int sum[N*4],num[N*4],tag[N*4],a[N];int f[N][100],cc[N][100],ex_c[N][100],pp[100];int n,m,p,c,k,i,opt,l,r;int quick_power(int x,int a,int mo){    int res = 1;    while (a) {if (a&1) res=(1ll*res*x)%mo; x=(1ll*x*x)%mo; a>>=1;}    return res;}void pushup(int x) {sum[x] = (sum[ls] + sum[rs]) % p; tag[x] = tag[ls] + tag[rs];}void build(int x,int l,int r){    if (l == r) {sum[x] = a[l]; num[x] = 0; tag[x] = r - l + 1; return;}    int mid = (l + r) >> 1; build(ls,l,mid); build(rs,mid+1,r);    pushup(x);}void change(int x,int l,int r,int L,int R){    if (L <= l && r <= R && !tag[x]) return;    if (l == r)         {            num[x]++; sum[x] = f[l][num[x]];            if (num[x] == k) tag[x]--;            return;        }    int mid = (l + r) >> 1;    if (L <= mid) change(ls,l,mid,L,R); if (mid < R) change(rs,mid+1,r,L,R);    pushup(x);}int query(int x,int l,int r,int L,int R){    if (L <= l && r <= R) return sum[x];    int mid = (l + r) >> 1; ll res = 0;    if (L <= mid) res += query(ls,l,mid,L,R);     if (mid < R) res += query(rs,mid+1,r,L,R);    return res % p;}int phi(int x){    int i,res = x;    fo(i,2,sqrt(x)) if (!(x%i)) {while (!(x%i)) x/=i;res = res/i*(i-1);}    if (x-1) res=res/x*(x-1);    return res;}int q_p(int x,int a,int mm){    if (a <= 10000) return cc[a][mm];    return (1ll*ex_c[a/10000][mm]*cc[a%10000][mm])%pp[mm];}int get_(int x,int num,int d){    int t;     if (!num) if (x > pp[d]) return x%pp[d]; else return x;    t = get_(x,num-1,d+1);    double q = log(1.0/x*pp[d+1])/log(1.0*c)-num+1;    if (q<=0) t += pp[d+1];    return q_p(c,t,d);}void pre(){    int i,j;    fo(i,0,10000)        fo(j,0,k)            {                cc[i][j] = quick_power(c,i,pp[j]);                int g = quick_power(c,10000,pp[j]);                ex_c[i][j] = quick_power(g,i,pp[j]);            }    fo(i,1,n)        if (a[i] == 0)            {                f[i][1] = 1;                fo(j,2,k)                    f[i][j] = get_(1,j-1,0);            }   else            {                fo(j,1,k)                    f[i][j] = get_(a[i],j,0);            }   }int main(){    scanf("%d%d%d%d",&n,&m,&p,&c);    k = 0; pp[0] = p;    while (pp[k]-1) {k++; pp[k] = phi(pp[k-1]);}    k++; pp[k] = 1;    fo(i,1,n) scanf("%d",&a[i]);    pre();    build(1,1,n);    while (m--)        {            scanf("%d%d%d",&opt,&l,&r);            if (opt == 0) change(1,1,n,l,r);            if (opt == 1) printf("%d\n",query(1,1,n,l,r));        }    //fo(i,l,r) printf("%d ",query(1,1,n,i,i));    return 0;}
0 0