zoukankan      html  css  js  c++  java
  • bzoj 4869: [Shoi2017]相逢是问候 [扩展欧拉定理 线段树]

    4869: [Shoi2017]相逢是问候

    题意:一个序列,支持区间(a_i leftarrow c^{a_i}),区间求和。在模p意义下。


    类似于开根操作,每次取phi在log次后就不变了。

    不互质怎么办? 我才知道,

    [n^x equiv n^{x mod varphi(p) + varphi(p)} pmod p, x ge varphi(p) ]

    不要求互质,只要求(x ge varphi(p))


    然后就很好做了...线段树维护每个点的操作次数和和,修改的时候每个点算一下,不变的区间不再更新。


    有一个问题,必须把(varphi(1)=1)也加进去。我想了好久好久...因为

    [0 < varphi(1) ,结果为0\ 2^x ge varphi(1), 结果为1 ]

    如果这个序列的数是0,再加一层之后和之前并不是不变的!


    然后需要给快速幂加点特技...

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    #define mid ((l+r)>>1)
    #define lc x<<1
    #define rc x<<1|1
    #define lson lc, l, mid
    #define rson rc, mid+1, r
    const int N = 5e4+5;
    inline int read() {
    	char c=getchar(); int x=0,f=1;
    	while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    	while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
    	return x*f;
    }
    
    int n, Q, p, c, a[N], op, l, r, phi[100], m, mo;
    int Phi(int n) {
    	int m = sqrt(n), ans = n;
    	for(int i=2; i<=m; i++) if(n % i == 0) {
    		ans = ans / i * (i-1);
    		while(n % i == 0) n /= i;
    	}
    	if(n > 1) ans = ans / n * (n-1);
    	return ans;
    }
    int Pow(ll a, int b, ll mo, bool &flag) {
    	ll ans = 1;
    	bool big = 0;
    	for(; b; b>>=1) {
    		if(b&1) {
    			ans = ans * a;
    			flag |= big | (ans >= mo);
    			ans %= mo;
    		}
    		a = a * a; if(a >= mo) big = 1, a %= mo;
    	}
    	return ans;
    }
    
    int cal(int x, int ci) {
    	if(x >= phi[ci]) x = x % phi[ci] + phi[ci];// flag = 1;
    	for(int i=ci-1; i>=0; i--) {
    		bool flag = 0;
    		x = Pow(c, x, phi[i], flag);
    		if(flag) x += phi[i];
    	}
    	return x % phi[0];
    }
    
    namespace S {
    	struct meow{int sum, ci;} t[N<<2];
    	void merge(int x) {
    		t[x].sum = (t[lc].sum + t[rc].sum) %mo;
    		t[x].ci = min(t[lc].ci, t[rc].ci);
    	}
    	void build(int x, int l, int r) {
    		if(l == r) t[x].sum = a[l];
    		else {
    			build(lson);
    			build(rson);
    			merge(x);
    		}
    	}
    	void cat(int x, int l, int r, int ql, int qr) {
    		if(t[x].ci >= m) return;
    		if(l == r) t[x].ci++, t[x].sum = cal(a[l], t[x].ci);
    		else {
    			if(ql <= mid) cat(lson, ql, qr);
    			if(mid < qr)  cat(rson, ql, qr);
    			merge(x);
    		}
    	}
    	int que(int x, int l, int r, int ql, int qr) {
    		if(ql<=l && r<=qr) return t[x].sum;
    		else {
    			int ans = 0;
    			if(ql <= mid) ans = (ans + que(lson, ql, qr)) %mo;
    			if(mid < qr)  ans = (ans + que(rson, ql, qr)) %mo;
    			return ans;
    		}
    	}
    }
    
    int main() {
    	freopen("in", "r", stdin);
    	n=read(); Q=read(); p=read(); c=read();
    	for(int i=1; i<=n; i++) a[i] = read();
    	mo = phi[0] = p;
    	while(p != 1) phi[++m] = p = Phi(p);
    	phi[++m] = 1;
    	S::build(1, 1, n);
    	for(int i=1; i<=Q; i++) {
    		op=read(); l=read(); r=read();
    		if(!op) S::cat(1, 1, n, l, r);
    		else printf("%d
    ", S::que(1, 1, n, l, r));
    	}
    }
    
    
  • 相关阅读:
    javascript实战演练,制作新按钮,‘新窗口打开网站’,点击打开新窗
    P1332 血色先锋队
    P4643 [国家集训队]阿狸和桃子的游戏
    T149876 公约数
    P1462 通往奥格瑞玛的道路
    P1083 借教室
    Tribles UVA
    Fence Repair POJ
    Crossing Rivers
    关于一轮
  • 原文地址:https://www.cnblogs.com/candy99/p/6771218.html
Copyright © 2011-2022 走看看