UESTC 87 Easy Problem With Numbers(扩展欧几里得+线段树)

题意:给出一个序列,对这个序列进行了三种操作,一种是查询。查询区间[l. r]内序列的乘积,一种是乘法修改,将区间[l. r]内全部元素乘以一个数,另一种操作是区间除法修改,保证除法合法,输出时将结果模除m(m可能为合数)。

思路:如果这道题m是质数那么就是一个裸的线段树,现在m可能为合数,逆元可能不存在,那么考虑把一个数a分解成b*c的形式,使得b是由m的质因子相乘得来,c与m互质,那么对于c我们就可以用线段树+扩展欧几里得求逆元来乱搞了。

现在问题是b,因为m的质因子的个数不超过10,我们可以考虑维护区间内m的每个质因子被乘的次数,然后查询的时候查询每个质因子被乘了多少次然后来一个快速幂,最后乘上之前那个互素的部分的结果就是最终答案。

注意:这道题有一个坑点,因为乘法操作时可能乘以0,而分解0的时候如果不特判会无限循环,因为0整除任意数,注意一下这点再细心点应该没啥问题了.....

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 10100;
//const int INF = 0x3f3f3f3f;
int n, m, p, a[MAXN], Q;
int prime[15], modify[15];
LL addv[15][4*MAXN], node[15][4*MAXN];
LL pow_mod(LL a, LL b) {
	if(b == 0) return 1;
	LL ans = pow_mod(a, b/2);
	ans = ans * ans % m;
	if(b&1) ans = ans * a % m;
	return ans; 
}
void build(int o, int L, int R) {
	for(int i = 0; i < p; i++) node[i][o] = 0, addv[i][o] = 0;
	node[p][o] = addv[p][o] = 1;
	if(L==R) {
		int tmp = a[L];
		for(int i = 0; i < p; i++) {
			while(tmp%prime[i] == 0) {
				tmp /= prime[i];
				node[i][o]++;
				addv[i][o]++;
			}
		}
		node[p][o] = tmp;
		addv[p][o] = tmp;
	}
	else {
		int M = (L+R) >> 1;
		build(2*o, L, M);
		build(2*o+1, M+1, R);
		for(int i = 0; i < p; i++) node[i][o] = node[i][o*2] + node[i][o*2+1];
		node[p][o] = node[p][2*o] * node[p][2*o+1] % m;
	}
}
void maintain(int o, int L, int R, int id) {
	if(id != p) node[id][o] = 0;
	else node[id][o] = 1;
	if(R > L) {
		int M = (L+R) >> 1;
		if(id != p) node[id][o] = node[id][2*o] + node[id][2*o+1];
		else node[id][o] = node[id][2*o]*node[id][2*o+1] % m;
	}
	if(id != p) node[id][o] += (R-L+1) * addv[id][o];
	else node[id][o] = node[id][o] * pow_mod(addv[id][o], R-L+1) % m;
}
void update(int o, int L, int R, int y1, int y2, int id, int v) {
	if(L>=y1 && R<=y2) {
		if(id != p) addv[id][o] += v;
		else addv[id][o] = addv[id][o] * v % m;
	}
	else {
		int M = (L+R) >> 1;
		if(y1 <= M) update(2*o, L, M, y1, y2, id, v);
		if(y2 > M) update(2*o+1, M+1, R, y1, y2, id, v);
	}
	maintain(o, L, R, id);
}
LL query(int o, int L, int R, int y1, int y2, int add, int id) {
	if(y1<=L && y2>=R) {
		if(id != p) return node[id][o] + add * (R-L+1);
		else return node[id][o] * pow_mod(add, R-L+1) % m;
	}
	else {
		int M = (L+R) >> 1;
		LL ans;
		if(id != p) ans = 0;
		else ans = 1;
		if(y1 <= M) {
			if(id != p) ans += query(2*o, L, M, y1, y2, add+addv[id][o], id);
			else ans = ans * query(2*o, L, M, y1, y2, add*addv[id][o]%m, id) % m;
		}
		if(y2 > M) {
			if(id != p) ans += query(2*o+1, M+1, R, y1, y2, add+addv[id][o], id); 
			else ans = ans * query(2*o+1, M+1, R, y1, y2, add*addv[id][o]%m, id) % m;
		} 
		return ans;
	}
}
int init_prime() {
	int sz = 0;
	int tmp = (int)sqrt(m+0.5), tm = m;
	for(int i = 2; i <= tmp; i++) {
		if(tm%i == 0) {
			prime[sz++] = i;
			while(tm % i == 0) tm /= i;
		}
	}
	if(tm > 1) prime[sz++] = tm;
	return sz;
}
void cal_modify(int v) {
	memset(modify, 0, sizeof(modify));
	modify[p] = 1;
	for(int i = 0; i < p; i++) {
		while(v%prime[i] == 0) {
			v /= prime[i];
			modify[i]++;
		}
	}
	modify[p] = v; 
}
void gcd(LL a, LL b, LL& d, LL& x, LL& y) {
	if(!b){ d = a; x = 1; y = 0; }
	else{ gcd(b, a%b, d, y, x); y -= x*(a/b); }
}
LL inv(LL a, LL n) {
	LL d, x, y;
	gcd(a, n, d, x, y);
	return (x+n)%n;
}
int main() {
    //freopen("input.txt", "r", stdin);
	int T, kase = 0; cin >> T;
	while(T--) {
		scanf("%d%d", &n, &m);
		p = init_prime();
		for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
		build(1, 1, n);
		printf("Case #%d:\n", ++kase);
		scanf("%d", &Q);
		char op[3];
		for(int i = 1, l, r, v; i <= Q; i++) {
			scanf("%s", op);
			if(op[0] == 'Q') {
				scanf("%d%d", &l, &r);
				LL ans = 1;
				for(int i = 0; i < p; i++) {
					ans = ans * pow_mod(prime[i], query(1, 1, n, l, r, 0, i)) % m;
				}
				ans = ans * query(1, 1, n, l, r, 1, p) % m;
				printf("%lld\n", ans);
			}
			else if(op[0] == 'M') {
				scanf("%d%d%d", &l, &r, &v);
				if(!v) {
					update(1, 1, n, l, r, p, 0);
					continue;
				}
				cal_modify(v);
				for(int i = 0; i < p; i++) if(modify[i]) update(1, 1, n, l, r, i, modify[i]);
				if(modify[p] > 1) update(1, 1, n, l, r, p, modify[p]);
			}
			else {
				scanf("%d%d%d", &l, &r, &v);
				cal_modify(v);
				for(int i = 0; i < p; i++) if(modify[i]) update(1, 1, n, l, r, i, -modify[i]);
				update(1, 1, n, l, r, p, inv(modify[p], m));
			}
		}	
	}
    return 0;
}



















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值