奇数国 \operatorname{奇数国} 奇数国
题目链接: luogu P4140 \operatorname{luogu\ P4140} luogu P4140 / ybt金牌导航8-4-5
题目
在一片美丽的大陆上有 100 000 100\,000 100000 个国家,记为 1 1 1 到 100 000 100\,000 100000 。这里经济发达,有数不尽的账房,并且每个国家有一个银行。
某大公司的领袖在这 100 000 100\,000 100000 个银行开户时都存了 3 3 3 大洋,他惜财如命,因此会不时地派小弟 GFS 清点一些银行的存款或者让 GFS 改变某个银行的存款。
该村子在财产上的求和运算等同于我们的乘法运算,也就是说领袖开户时的存款总和为 3 100000 3^{100000} 3100000 。这里发行的软妹面额是最小的 60 60 60 个素数( p 1 = 2 , p 2 = 3 , … , p 60 = 281 p_1=2,p_2=3,\ldots, p_{60}=281 p1=2,p2=3,…,p60=281 ),任何人的财产都只能由这 60 60 60 个基本面额表示,即设某个人的财产为 f o r t u n e fortune fortune (正整数),则 f o r t u n e = p 1 k 1 × p 2 k 2 × … p 60 k 60 fortune=p_1^{k_1} \times p_2^{k_2} \times \ldots p_{60}^{k_{60}} fortune=p1k1×p2k2×…p60k60 。
领袖习惯将一段编号连续的银行里的存款拿到一个账房去清点,为了避免 GFS 串通账房叛变,所以他不会每次都选择同一个账房。GFS 跟随领袖多年已经摸清了门路,知道领袖选择账房的方式。如果领袖选择清点编号在 [ a , b ] [a,b] [a,b] 内的银行财产,他会先对 [ a , b ] [a,b] [a,b] 的财产求和(记为 p r o d u c t product product ),然后在编号属于 [ 1 , p r o d u c t ] [1,product] [1,product] 的账房中选择一个去清点存款,检验自己计算是否正确同时也检验账房与 GFS 是否有勾结。GFS 发现如果某个账房的编号 n u m b e r number number 与 p r o d u c t product product 相冲,领袖绝对不会选择这个账房。
怎样才算与 p r o d u c t product product 不相冲呢?若存在整数 x , y x,y x,y 使得 n u m b e r × x + p r o d u c t × y = 1 number \times x+product \times y=1 number×x+product×y=1 ,那么我们称 n u m b e r number number 与 p r o d u c t product product 不相冲,即该账房有可能被领袖相中。当领袖又赚大钱了的时候,他会在某个银行改变存款,这样一来相同区间的银行在不同的时候算出来的 p r o d u c t product product 可能是不一样的,而且领袖不会在某个银行的存款总数超过 1 0 6 10^6 106 。
现在 GFS 预先知道了领袖的清点存款与变动存款的计划,想请你告诉他,每次清点存款时领袖有多少个账房可以供他选择,当然这个值可能非常大,GFS 只想知道对 19 961 993 19\,961\,993 19961993 取模后的答案。
输入
第一行一个整数 x x x 表示领袖清点和变动存款的总次数。
接下来 x x x 行,每行 3 3 3 个整数 a i , b i , c i a_i,b_i,c_i ai,bi,ci 。 a i a_i ai 为 0 0 0 时表示该条记录是清点计划,领袖会清点 b i b_i bi 到 c i c_i ci 的银行存款,你需要对该条记录计算出 GFS 想要的答案。 a i a_i ai 为 1 1 1 时表示该条记录是存款变动,你要把银行 b i b_i bi 的存款改为 c i c_i ci ,不需要对该记录进行计算。
输出
对于每个询问,输出一行一个整数表示答案。
样例输入
6
0 1 3
1 1 5
0 1 3
1 1 7
0 1 3
0 2 3
样例输出
18
24
36
6
样例解释
初始化每个国家存款都为
3
3
3 ;
1
1
1 到
3
3
3 的
p
r
o
d
u
c
t
product
product 为
27
27
27 ,
[
1
,
27
]
[1,27]
[1,27] 与
27
27
27 不相冲的有
18
18
18 个数;
1
1
1 的存款变为
5
5
5 ;
1
1
1 到
3
3
3 的
p
r
o
d
u
c
t
product
product 为
45
45
45 ,
[
1
,
45
]
[1,45]
[1,45] 与
45
45
45 不相冲的有
24
24
24 个数;
1
1
1 的存款变为
7
7
7 ;
1
1
1 到
3
3
3 的
p
r
o
d
u
c
t
product
product 为
63
63
63 ,
[
1
,
63
]
[1,63]
[1,63] 与
63
63
63 不相冲的有
36
36
36 个数;
2
2
2 到
3
3
3 的
p
r
o
d
u
c
t
product
product 为
9
9
9,
[
1
,
9
]
[1,9]
[1,9] 与
9
9
9 不相冲的有
6
6
6 个数。
数据范围
所有数据均满足: x ≥ 1 x \geq 1 x≥1 , c i − b i ≥ 0 c_i -b_i \geq 0 ci−bi≥0 。
子任务编号 | 分值 | x ≤ x\leq x≤ | c i − b i ≤ c_i-b_i\leq ci−bi≤ | 特殊性质 |
---|---|---|---|---|
1 1 1 | 20 20 20 | 1 0 4 10^4 104 | 100 100 100 | 有 |
2 2 2 | 30 30 30 | 5 ∗ 1 0 4 5*10^4 5∗104 | 1 0 4 10^4 104 | 无 |
3 3 3 | 50 50 50 | 1 0 5 10^5 105 | 1 0 5 10^5 105 | 无 |
特殊性质指:所有 p r o d u c t ≤ 1 0 18 product \leq 10^{18} product≤1018 。
思路
这道题就是数学题,不过要用线段树来维护。
我们可以发现,其实这道题要求的就是区间的积的欧拉函数。
众所周知(但是我不知 ) ,欧拉函数可以这么求:
ϕ
(
n
)
=
n
(
1
−
1
p
1
)
(
1
−
1
p
2
)
.
.
.
(
1
−
1
p
i
)
ϕ(n)=n(1− \frac{1}{p_1})(1− \frac{1}{p_2})...(1− \frac{1}{p_i})
ϕ(n)=n(1−p11)(1−p21)...(1−pi1)
(
p
i
p_i
pi 表示
n
n
n 的每个质因子)
因为题目说
n
n
n 只由前六十个素数组成,那这种方法就是可行的。
这六十个素数和他们的逆元可以预处理,可以求出,也可以直接打表,反正我这里打表了。
至于会修改点的值和求区间的积,我们就可以发现其实完全可以用线段树来求。那我们就用线段树来维护区间的积和这个积的质因子。(用二进制来存, 2 60 2^{60} 260 可以用 l o n g l o n g long\ long long long 存下)
那基本就这样了。
代码
#include<cstdio>
#define ll long long
#define mo 19961993
using namespace std;
//预处理(打表)出素数和其逆元
ll su[] = {0, 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};
ll ni[] = {0, 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};
bool susu[301];
int n, x, y, z;
ll ans, gtree, gmi;
struct node {
ll sum, mi;
}tree[400001];
int ksm(int x, int y) {
if (!y) return 1;
int re = 1;
for (; y; y >>= 1) {
if (y & 1) re = (re * x) % mo;
x = (x * x) % mo;
}
return re;
}
void build(int l, int r, int now) {//建树
if (l == r) {
tree[now].sum = 3;
tree[now].mi = 2;
return ;
}
int mid = (l + r) / 2;
build(l, mid, now * 2);
build(mid + 1, r, now * 2 + 1);
tree[now].sum = (tree[now * 2].sum * tree[now * 2 + 1].sum) % mo;
tree[now].mi = 2;
}
void change(int now, int l, int r, int x, int num) {//单点修改值
if (l == r) {
tree[now].sum = num;
tree[now].mi = 0;
for (int i = 1; i <= 60; i++)
if ((num % su[i]) == 0) tree[now].mi |= 1ll << (i - 1);
return ;
}
int mid = (l + r) / 2;
if (x <= mid) change(now * 2, l, mid, x, num);
else change(now * 2 + 1, mid + 1, r, x, num);
tree[now].sum = (tree[now * 2].sum * tree[now * 2 + 1].sum) % mo;
tree[now].mi = tree[now * 2].mi | tree[now * 2 + 1].mi;
}
void getans(int now, int l, int r, int al, int ar) {//区间查询
if (l >= al && r <= ar) {
gtree = (gtree * tree[now].sum) % mo;
gmi |= tree[now].mi;
return ;
}
int mid = (l + r) / 2;
if (al <= mid) getans(now * 2, l, mid, al, ar);
if (mid < ar) getans(now * 2 + 1, mid + 1, r, al, ar);
}
int main() {
build(1, 100000, 1);//建树
scanf("%d", &n);//读入
for (int i = 1; i <= n; i++) {
scanf("%d %d %d", &z, &x, &y);//读入
if (z) change(1, 1, 100000, x, y);//单点改权值
else {
gtree = 1;
gmi = 0;
getans(1, 1, 100000, x, y);//区间查询
ans = gtree;
for (int i = 1; i <= 60; i++)
if ((1ll << (i - 1)) & gmi) {//找到里面有的素数
ans = (ans * (su[i] - 1)) % mo;
ans = (ans * ni[i]) % mo;
}
printf("%lld\n", ans);//输出
}
}
return 0;
}