码迷,mamicode.com
首页 > 其他好文 > 详细

[六省联考2017]相逢是问候(扩展欧拉定理+预处理幂)

时间:2020-04-16 22:10:59      阅读:72      评论:0      收藏:0      [点我收藏+]

标签:sub   pow   多少   its   code   int   com   efi   main   

https://www.luogu.com.cn/problem/P3747

已经记不请上一次遇到扩展欧拉定理是什么时候了。

\(a^b~mod~p=\)
\(b<\phi(p),a^b~mod~p\)
\(b\ge \phi(p),a^{b~mod~\phi(p)+\phi(p)}~mod~p\)

假设模数一直取\(\phi\)的序列为\(p[1],p[2],…,p[p0](p0 \le 2log_2(mo))\)

对于这题,可以对每个数记录一个\(cnt\)表示已经pow了多少次,当\(cnt>p0\)后,都和\(cnt=p0+1\)时的值一样

注意是和\(cnt=p0+1\)时的值一样,为了方便,多开一位:p[++p0]=1

用线段树维护区间\(cnt\)最小值,如果\(\le p0\),就下去修改就好了。

每个数要需要修改\(log\)次,每次修改要\(log^2\)的时间求新的答案(一个log来自快速幂),这样会T两个点。

注意到底数都是\(c\),所以预处理\(c^{0..15000}\)\(c^{0..15000*15000}\)\(mod\)每个\(p[i]\)下的值,就可以\(O(2)\)快速幂。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 50005;

int n, m, sp, c;
int v[N];

int phi(int n) {
	if(n == 1) return 1;
	int s = n;
	for(int x = 2; x * x <= n; x ++) if(n % x == 0) {
		s = s / x * (x - 1);
		for(; n % x == 0; n /= x);
	}
	if(n > 1) s = s / n * (n - 1);
	return s;
}

int p[N], p0;

namespace sub1 {
	struct P {
		ll x; int y;
		P(ll _x = 0, int _y = 0) {
			x = _x, y = _y;
		}
	};
	P mul(P a, P b, int mo) {
		a.y |= b.y;
		a.x *= b.x;
		if(a.x >= mo) a.x %= mo, a.y = 1;
		return a;
	}
	
	const int M = 15000;
	
	P t1[100][M], t2[100][M];
	
	void build() {
		fo(i, 1, p0) {
			t1[i][0] = t2[i][0] = P(1 % p[1], 1 >= p[1]);
			P w = P(c % p[i], c >= p[i]);
			fo(j, 1, M) t1[i][j] = mul(t1[i][j - 1], w, p[i]);
			w = t1[i][M];
			fo(j, 1, M) t2[i][j] = mul(t2[i][j - 1], w, p[i]);
		}
	}
	
	P b[N];
	ll calc(int *a, int a0) {
		a0 = min(a0, p0);
		b[a0] = P(a[a0] % p[a0], a[a0] >= p[a0]);
		fd(i, a0 - 1, 1) {
			int y = b[i + 1].x + (b[i + 1].y ? p[i + 1] : 0);
			int z1 = y % M, z2 = y / M;
			b[i] = mul(t1[i][z1], t2[i][z2], p[i]);
		}
		return b[1].x;
	}
}

using sub1 :: calc;

int a[N]; int a0;

#define i0 i + i
#define i1 i + i + 1
ll t[N * 4]; int mi[N * 4];

void bt(int i, int x, int y) {
	if(x == y) {
		t[i] = v[x] % p[1];
		return;
	}
	int m = x + y >> 1;
	bt(i0, x, m); bt(i1, m + 1, y);
	t[i] = (t[i0] + t[i1]) % p[1];
}
int pl, pr, px;
void add(int i, int x, int y) {
	if(mi[i] >= p0 - 1 || y < pl || x > pr) return;
	if(x == y) {
		mi[i] ++;
		a0 = mi[i] + 1;
		fo(j, 1, a0 - 1) a[j] = c; a[a0] = v[x];
		t[i] = calc(a, a0);
		return;
	}
	int m = x + y >> 1;
	add(i0, x, m); add(i1, m + 1, y);
	t[i] = (t[i0] + t[i1]) % p[1];
	mi[i] = min(mi[i0], mi[i1]);
}
void ft(int i, int x, int y) {
	if(y < pl || x > pr) return;
	if(x >= pl && y <= pr) {
		px = (px + t[i]) % p[1];
		return;
	}
	int m = x + y >> 1;
	ft(i0, x, m); ft(i1, m + 1, y);
}

int op;

int main() {
	scanf("%d %d %d %d", &n, &m, &sp, &c);
	p[p0 = 1] = sp;
	while(p[p0] != 1) p0 ++, p[p0] = phi(p[p0 - 1]);
	p[++ p0] = 1;
	fo(i, 1, n) scanf("%d", &v[i]);
	sub1 :: build();
	bt(1, 1, n);
	fo(ii, 1, m) {
		scanf("%d %d %d", &op, &pl, &pr);
		if(op == 0) {
			add(1, 1, n);
		} else {
			px = 0;
			ft(1, 1, n);
			pp("%d\n", px);
		}
	}
}

[六省联考2017]相逢是问候(扩展欧拉定理+预处理幂)

标签:sub   pow   多少   its   code   int   com   efi   main   

原文地址:https://www.cnblogs.com/coldchair/p/12716074.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!