【Luogu P4140】奇数国

链接:

题目

博客园

题目大意:

有 \(10^5\) 个数,一开始都是 \(3\),现在有两个操作:

  1. 给定区间 \([l,r]\),求它的积的欧拉函数值。

  2. 将第 \(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;
}
上一篇:习题:DZY Loves Math IV(杜教筛)


下一篇:LaTeX中和Word中打公式的区别,求和符号