【Luogu P4140】奇数国
链接:
题目大意:
有 \(10^5\) 个数,一开始都是 \(3\),现在有两个操作:
-
给定区间 \([l,r]\),求它的积的欧拉函数值。
-
将第 \(x\) 个数改为 \(y\)。
结果对 \(19961993\) 取模。
正文:
本题的难点在于怎么求出 \(\varphi(product)\)。
题目中特意提示了:
这里发行的软妹面额是最小的 \(60\) 个素数 \((p_1=2,p_2=3,\cdots,p_{60}=281)\),任何人的财产都只能由这 \(60\) 个基本面额表示。
也就说,我们可以通过枚举质因子的那个公式求解:
\[\varphi(n)=n\prod_{i|n}\left(1-\frac{1}{p_i}\right)
\]
我们可以通过线段树维护区间积求解出 \(product\),但是它的质因数呢?
仍然用线段树维护。因为至多 \(60\) 个,我们就能用状压了。
如果先把质数和逆元预处理好,那么求解 \(\varphi\) 值的时间复杂度就是 \(O(60)\) 了。
代码:
const ll mod = 19961993;
const 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};
const ll inv[] = {9980997,6653998,11977196,8555140,5444180,1535538,10568114,14708837,3471651,11701858,17386252,1618540,16066970,2321162,18263100,16948862,12518538,15380552,10725847,1686929,13399146,17182475,12025297,15924736,13582387,395287,6395590,15857658,16299242,6359573,3300802,18742940,6702567,10914471,16210746,11765678,5340151,18247466,7769638,8077107,11932588,6506948,1985748,6619521,5877135,4413707,9744480,10115270,14597757,16475182,18334191,5011379,18885205,7555336,621385,11309266,12170137,12006660,18304499,11153142};
int m;
int n = 100000;
struct SegmentTree
{
struct Seg
{
int l, r;
ll val, pri;
}t[N << 2];
void build (int p, int l, int r)
{
t[p].l = l, t[p].r = r;
if (l == r)
{
t[p].val = 3, t[p].pri = 2;
return ;
}
int mid = t[p].l + t[p].r >> 1;
build (p << 1, l, mid);
build (p << 1 | 1, mid + 1, r);
t[p].val = t[p << 1].val * t[p << 1 | 1].val % mod;
t[p].pri = t[p << 1].pri | t[p << 1 | 1].pri;
}
void change (int p, int x, ll val, ll P)
{
if (t[p].l == t[p].r)
{
t[p].val = val, t[p].pri = P;
return ;
}
int mid = t[p].l + t[p].r >> 1;
if (t[p].l <= x && x <= mid) change (p << 1, x, val, P);
if (mid + 1 <= x && x <= t[p].r) change (p << 1 | 1, x, val, P);
t[p].val = t[p << 1].val * t[p << 1 | 1].val % mod;
t[p].pri = t[p << 1].pri | t[p << 1 | 1].pri;
}
ll query (int p, int l, int r, int opt)
{
if (l <= t[p].l && t[p].r <= r)
return opt? t[p].val: t[p].pri;
int mid = t[p].l + t[p].r >> 1;
ll ans = opt;
if (l <= mid) ans = opt? ans * query(p << 1, l, r, opt) % mod: ans | query(p << 1, l, r, opt);
if (mid < r) ans = opt? ans * query(p << 1 | 1, l, r, opt) % mod: ans | query(p << 1 | 1, l, r, opt);
return ans;
}
}t;
ll con(ll x)
{
ll tmp = 0;
for (ll i = 0; i < 60; i++)
if(!(x % pri[i])) tmp |= (1ll << i);
return tmp;
}
int main()
{
scanf ("%d", &m);
t.build(1, 1, n);
while (m--)
{
int opt;
ll x, y;
scanf ("%d%lld%lld", &opt, &x, &y);
if (opt == 1) t.change(1, (int)x, y, con(y));
else
{
ll tmp = t.query(1, (int)x, (int) y, 0);
ll ans = t.query(1, (int)x, (int) y, 1);
for (ll i = 0; i < 60; i++)
if(tmp & (1ll << i))
ans = (ans * inv[i]) % mod, ans = ans * (pri[i] - 1) % mod;
printf("%lld\n", ans);
}
}
return 0;
}