版权声明: https://blog.csdn.net/moon_sky1999/article/details/87351751
题目来源:http://codeforces.com/contest/1114/problem/F
题意:
给定一个序列c,给定两种操作
(1)对区间[l,r]中所有元素*x。
(2)查询区间[l,r]中所有元素成积的欧拉函数值。
数组c的所有元素均<300,每次乘操作的x也均<300。
可以预先得知,300以内的素数共有62个,可以预先保存到数组中。
求取区间内所有数的欧拉函数值,可以将区间所有数的乘积转成p1^q1*p2^q2*...*pn^qn,其中p1~pn均是素数,这样欧拉函数值就是 总乘积*(p1-1)/p1*(p2-1)/p2*...*(pn-1)/pn。
除法操作可以转为乘1e9+7意义下的逆元,可以预先处理得到。
现在即须知道区间内出现了多少种素数。
由于素数最多仅有62种,可以用状压的方法,压为一个long long的数,表示该区间素数存在情况。
这样,线段树维护两个量:区间总乘积sum和区间素数出现情况pos,以及相应的lazy标记。(代码中add对饮sum的,lz对应pos的)
线段树维护区间内所有数均*一个数的操作与+类似,维护pos也即维护区间或(or)的值。
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 4e5 + 10;
int a[maxn], n, q;
ll pri[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223,
227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293};
ll ni[65];//保存逆元
ll pow_mod(ll a, ll b, ll m) {
ll ans = 1;
while (b) {
if (b & 1)ans = ans * a % m;
a = a * a % m;
b >>= 1;
}
return ans;
}
struct tree {
int left, right;
ll sum, add;
ll pos, lz;
} c[maxn << 2];
void build(int id, int l, int r) {
c[id].left = l;
c[id].right = r;
c[id].add = 1;
c[id].lz = 0;
if (l == r) {
c[id].sum = a[l];
for (ll i = 0; i < 62; ++i) {
if (a[l] % pri[i] == 0) {
c[id].pos = (c[id].pos | (1ll << i));
a[l] /= pri[i];
while (a[l] % pri[i] == 0)a[l] /= pri[i];
}
if (a[l] < pri[i])break;
}
return;
}
int mid = (l + r) >> 1;
build(id << 1, l, mid);
build(id << 1 | 1, mid + 1, r);
c[id].pos = ((c[id << 1].pos) | (c[id << 1 | 1].pos));
c[id].sum = (c[id << 1].sum * c[id << 1 | 1].sum) % mod;
}
void pushdown(int id) {
c[id << 1].sum =
(c[id << 1].sum * pow_mod(c[id].add, (c[id << 1].right - c[id << 1].left + 1), mod)) % mod;
c[id << 1 | 1].sum =
(c[id << 1 | 1].sum * pow_mod(c[id].add, (c[id << 1 | 1].right - c[id << 1 | 1].left + 1), mod)) % mod;
c[id << 1].add = c[id << 1].add * c[id].add % mod;
c[id << 1 | 1].add = c[id << 1 | 1].add * c[id].add % mod;
c[id].add = 1;
c[id << 1].pos = (c[id << 1].pos | c[id].lz);
c[id << 1 | 1].pos = (c[id << 1 | 1].pos | c[id].lz);
c[id << 1].lz = (c[id << 1].lz | c[id].lz);
c[id << 1 | 1].lz = (c[id << 1 | 1].lz | c[id].lz);
c[id].lz = 0;
}
void update(int id, int l, int r, ll v, ll p) {
if (c[id].left > r || c[id].right < l)return;
if (c[id].left >= l && c[id].right <= r) {
c[id].sum = (pow_mod(v, (c[id].right - c[id].left + 1), mod) * c[id].sum) % mod;
c[id].add = (c[id].add * v) % mod;
c[id].pos = (c[id].pos | p);
c[id].lz = (p | c[id].lz);
return;
}
if (c[id].add > 1)pushdown(id);
if (c[id].lz)pushdown(id);
update(id << 1, l, r, v, p);
update(id << 1 | 1, l, r, v, p);
c[id].sum = c[id << 1].sum * c[id << 1 | 1].sum % mod;
c[id].pos = (c[id << 1].pos | c[id << 1 | 1].pos);
}
ll query_sum(int id, int l, int r) {
if (r < c[id].left || l > c[id].right)return 1;
if (c[id].left >= l && c[id].right <= r) {
return c[id].sum;
}
if (c[id].add>1)pushdown(id);
ll ans = query_sum(id << 1, l, r) * query_sum(id << 1 | 1, l, r) % mod;
c[id].sum = c[id << 1].sum * c[id << 1 | 1].sum % mod;
return ans;
}
ll query_pos(int id, int l, int r) {
if (r < c[id].left || l > c[id].right)return 0;
if (c[id].left >= l && c[id].right <= r) {
return c[id].pos;
}
if (c[id].lz)pushdown(id);
ll pos = (query_pos(id << 1, l, r) | query_pos(id << 1 | 1, l, r));
c[id].pos = (c[id << 1].pos | c[id << 1 | 1].pos);
return pos;
}
char s[10];
int main() {
for (int i = 0; i < 62; ++i) {
ni[i] = pow_mod(pri[i], mod - 2, mod);
}
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
build(1, 1, n);
int l, r, x;
ll ans, p;
while (q--) {
scanf("%s", s);
if (s[0] == 'T') {
scanf("%d%d", &l, &r);
ans = query_sum(1, l, r);
p = query_pos(1, l, r);
for (ll i = 0; i < 62; ++i) {
if ((p & (1ll << i)) != 0) {
ans = ans * ni[i] % mod;
ans = ans * (pri[i] - 1) % mod;
}
}
printf("%lld\n", ans);
} else {
scanf("%d%d%d", &l, &r, &x);
ll p = 0;
ll v = x;
for (ll i = 0; i < 62; ++i) {
if (v % pri[i] == 0) {
p = (p | (1ll << i));
v /= pri[i];
while (v % pri[i] == 0) v /= pri[i];
}
if (pri[i] > v)break;
}
update(1, l, r, x, p);
}
}
return 0;
}