阅读视图

发现新文章,点击刷新页面。
🔲 ☆

退群杯——从闲聊到现实

<blockquote><p>各位 OI 退役或还没退役的选手、各位对 XCPC 感兴趣或已经参与过的选手、各位仍在关注退群杯的同学<del>以及各位乐子人</del>:</p><p>首先祝大家新年快乐!我们非常荣幸地在此宣布,经过两个多月的前期筹备,第三届「退群杯」竞赛(XCPC 分场)即将与大家见面!</p><p>本届「退群杯」与前两届不同,其内容为<strong>算法竞赛</strong>,赛制则采取大家所熟悉<del>如果不熟悉就现在熟悉</del>的 XCPC 赛制,但改为<strong>单人参赛</strong>。正赛共⑨题,时长 5 小时,计划于 2 月 12 日下午进行。为方便各位选手熟悉赛制和办赛平台,正赛前一天即 2 月 11 日将有一场时长 2 小时的热身赛。赛后,我们将公开题解(PDF 格式)。如条件允许,我们也将准备一场直播讲解。</p><p>与第二届「退群杯」一样,本届「退群杯」也将有伴随题目的剧情。我们力图为大家带来更多参赛乐趣,得益于算法竞赛题目相对较高的自由度,解谜剧情将与题目充分结合。不过请各位选手放心,即使完全不看剧情也不会影响 AK 解题。</p><p>本次比赛我们为各位参赛选手准备了丰富的奖励,包括但不限于马克杯退群杯、桌垫、亚克力挂件,解谜成功更有机会领取神秘奖品!</p><p>退役 OIer 文化课交流群 第三届「退群杯」全体 STAFF</p><p>2022 年 2 月 1 日</p></blockquote><p>「退群杯」中的「退群」二字,可能与大家所熟悉的退群含义不同,指的是退役 OIer 文化课交流群(群号 1107305016,其实由于 OIer 的不断迭代,其实也已经成为了一个社交文化圈,并衍生出了若干子群,退群杯群就是其中一个)。</p><p>由于自招取消,强基计划开始,高中生们也开始用脚投票,高中生 OIer 也越来越少。第三届「退群杯」即将开始,作为第一届第二届「退群杯」英语科出题人(前两届是文化课模拟考试,第三届开始为 XCPC 类考试),很高兴学弟们接过了「退群杯」的接力棒,让这种 Oier 学长学姐带学弟学妹的风气继续保持下去。</p><h2 id="从无到有——第一届退群杯"><a href="#从无到有——第一届退群杯" class="headerlink" title="从无到有——第一届退群杯"></a>从无到有——第一届退群杯</h2><p>由于主群退群「退役 OIer 文化课交流群」迭代了数次,导致一些历史不可考,此处仅能通过一部分聊天记录和个人回忆来还原历史。</p><p>其他组的成员看到都有在<a href="https://www.zhihu.com/question/371927453">知乎上</a>发表自己角度的经历,我也分享一下我的经历。</p><p>当时看到了主群的群公告,征集一些群友一起给 20 级高中毕业生出套模拟题。当时正值疫情最严重的时候,各地也没法如期开学,大学生们也省去了各种各样非必要的活动,剩下的时间正好可以用来出题,于是我报名了英语组,也成为了第一届退群杯的英语组负责人。</p><p>当时一起去完成的有六七人,每人从选素材到高考题型的适配化,最后拼在一起,就形成了一套英语科试题。对于听力,当时没有条件去录音(我们的口音也不合适),然后我们最后的决定是找一些听力题拼在一起,我使用 Au 把各段听力进行了拼接,把 ksyx 用谷歌娘读的考试名称组装在一起,导出了听力音频。</p><p>当初一个组的 SDUer 还有 Kamigen 和 Raffica,在后期高考结束志愿选择的时候,我们三位与其他群友劝退自己学校的行为相反,非常欢迎大家来 SDU,于是我们三位的行为就非常亮眼,成为了退群杯群里的 SDU 三人组。</p><p>在这次英语出题里,我记得我们还借此成立了一个英语口语交流组,以讨论班的形式举行。每位同学准备一次讨论的材料,然后其他人就会一起用英语讨论这些话题。当时因为有泰山学堂周小兰老师的英语口语课,我就拉了学堂的 Arno 同学,一起参与这个口语交流。当然在英语口语课结束之后那个群就长期咕咕咕了<span class="github-emoji"><span>😎</span><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f60e.png?v8" aria-hidden="true" onerror="this.parent.classList.add('github-emoji-fallback')"></span>。</p><p>记得在公告之前,大家讨论宣传文案,记得当时借鉴的文案来自 UOJ——</p><blockquote><p>10 月 26 号,星期日晚上 7:00~10:00 开始公测!</p><p>UOJ 就要迈出第一步了!欢迎大家来捧场!</p><p>由于是公测,目的主要在于测 BUG,所以是人民群众喜闻乐见的原题大战。</p><p>不过不用担心!我觉得我选的题还是蛮好玩的!</p><p>为了应景,题目难度、部分分设置都和联赛差不多,对于想要为 NOIP 准备的同学而言是个不可错过的机会!</p><p>赛后我们会有详细的题解。</p><p>出题人有 ydc, vfleaking。</p><p>题目三小时三道题的 OI 赛制,</p><p>难度高仿 noip,</p><p>大家也不用担心掉 rating,</p><p>因为这一场是 unrated 哟,还怕什么!</p><p>这是神犇强者展现实力,虐爆全场的时候!</p><p>这是大众选手增强信心,迈向未来的时候!</p><p>还等什么?来战吧!</p><p>有什么问题请在下面留言。</p></blockquote><p>最后一版文案如下(还好当时截了个图,不然真就没记录了 2333)</p><img src="../images/2022020301.jpg" alt="第一届退群杯公告文案" style="zoom: 33%;"><p>以及当时做的宣传图,宣传图好像是 Kamigen 同学使用 SCP 同人图 P 出来的</p><img src="../images/2022020302.jpg" alt="第一届退群杯宣传海报" style="zoom: 50%;"><p>Kamigen 小姐姐真的很认真负责,最后英语科试题的讲评是她讲的,虽然由于设备原因,笔记本噪音比较大,但是小姐姐甜美的嗓音还是非常动人的(</p><p>我记得当时答题卡,一直不知道该怎么还原一个好看的答题卡,于是我盯上了当时高中用的“好分数”平台。果然高中班主任老师的密码还是 6 位数生日,然后我就用他的权限做了各科的答题卡,导出后删除(抹除痕迹)。</p><h2 id="从小试牛刀到一次突破——第二届退群杯"><a href="#从小试牛刀到一次突破——第二届退群杯" class="headerlink" title="从小试牛刀到一次突破——第二届退群杯"></a>从小试牛刀到一次突破——第二届退群杯</h2><p>第二年,出题组们又开始“搞事情”了。今年大家想做一个“剧情向”的比赛,大家按照不同的顺序完成题目,可以解锁不同的剧情。</p><p>在这一年,出题组里又来了许多新血液——例如第一届退群杯受众 MikuNotFound 同学,搭建了第二届退群杯的前端。</p><p>这一次,退群杯甚至有了<a href="https://tqb.kskun.com/">官网</a>,用的是 KS 同学的域名和后端。大家可以通过在界面上注册登录进行做题,解锁剧情。</p><p>在<a href="https://tqb.kskun.com/doc/staff">staff 列表</a>的同学们越来越多啦,大家不光可以展示自己的 id 和头像,还可以放上一句话简介。数学组比较会玩,而英语组就没能提前商量好<span class="github-emoji"><span>😰</span><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f630.png?v8" aria-hidden="true" onerror="this.parent.classList.add('github-emoji-fallback')"></span>。</p><img src="../images/2022020303.png" alt="命题组名单节选" style="zoom:67%;"><p>附第二届退群杯相关知乎文档</p><p><a href="https://zhuanlan.zhihu.com/p/350271701">2021 年退役 OIer 文化课交流群联合竞赛暨第二届“退群杯”预告</a></p><p><a href="https://zhuanlan.zhihu.com/p/343210737">第二届退群杯的一些近况</a></p><p><a href="https://zhuanlan.zhihu.com/p/353375513">第二届“退群杯”正式上线公告</a></p><p><a href="https://zhuanlan.zhihu.com/p/351085436">第二届“退群杯”剧情与数值系统前瞻</a></p>
🔲 ⭐

2021ICPC网络赛 - L. Euler Function

题目概览

image-20210927164841332

思路

考场上作为队伍$ds$选手,写这个题时候把队友演了,虽然知道是处理$25$个线段树,但是一直没调出来(

考后补题,整理如下。

前置知识:image-20210927170114944


首先观察到最开始时候$1 \leq x_i \leq 100$​,而且$100$​以内素数只有$25$​​个,考虑这是一个突破点。

假设说要对区间$[L,R]$​​内所有数都乘上一个数字$W$,分两种情况:

  • 假设数字为$W=p_1^{k_1} \times p_2^{k_2} \times \dots p_l^{k_l}$,且区间$[L,R]$内所有数均含有质因子$p_1,p_2,\dots,p_l$​时​

    我们考虑分开算每个质因子的贡献:

    如:$[2 \times 2 \times 3,2 \times 3,2 \times 3 \times 3]$​,即$[12,6,18]$​,我们可以拆为:

    • $p=2:[\phi(4)=2, \phi(2)=1, \phi(2)=1]$
    • $p=3:[\phi(3)=2,\phi(3)=2, \phi(9)=6]$​

    由于任意两个质因数间一定互质,且$gcd(n,m)=1$时,$\phi(nm)=\phi(n)\phi(m)$,原数组的欧拉函数值的和为各个质因子对应的欧拉函数的积。

    即:$[\phi(12)=\phi(4) \times \phi(3), \phi(6)=\phi(2) \times \phi(3), \phi(18)=\phi(2) \times \phi(9)]$​

    又因为区间$[L,R]$​内均含有所乘的数的质因子$p_1,p_2,\dots,p_l$​,且当$n=p^k$​时,$\phi(n)=(p-1)p^{k-1}$​,那么在计算区间乘的贡献时,其实只要对$[L,R]$​的区间和乘上一个$W$​就可以了,用上面的数组$[12,6,18]$​​举例如下:

    假设$W=6=2 \times 3$​​,结果数组为$[12 \times 6,6 \times 6,18 \times 6]$,即:$[72,36,108]$,对应的欧拉函数为$[24,12,36]$;

    拆开来看,有:

    • $p=2:[\phi(4 \times 2) = \phi(4) \times 2 = 4, \phi(2 \times 2) = \phi(2) \times 2 = 2, \phi(2 \times 2)=\phi(2) \times 2 = 2]$​
    • $p=3:[\phi(3 \times 3)=\phi(3) \times 3=6,\phi(3 \times 3)=\phi(3) \times 3=6, \phi(9 \times 3)=\phi(9) \times 3=18]$​

    即$[\phi(8)\times\phi(9)=24,\phi(4)\times\phi(9)=12,\phi(4)\times\phi(27)=3]$​

    也就是说,这种情况下,你只需要对整个区间的和乘上$W$,得到的就是最终答案。

  • 如果区间$[L,R]$内存在数字并不都含有$W$的全部质因数呢?

    只要对区间内这些并不含有$W$的全部质因数的数字暴力更新就可以了。

    由于$1\leq x_i,w_i \leq 100$,质因数一定在$100$以内,也就是说最多只有$25$个质因子。

    考虑最坏情况,$25$个质因子暴力更新,所需要的也只是$25n$​次操作而已,之后只要用线段树维护区间乘和区间和就可以了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/* vegetable1024 | Maybe Lovely? */

#include <bits/stdc++.h>
#define maxn 800005
#define int long long
#define lson(x) (x << 1)
#define rson(x) (x << 1 | 1)
using namespace std;

//DEBUG TEMPLATE
template<typename T>
void Print(T value){
std::cout << value << '\n';
}
template<typename Head, typename... Rail>
void Print(Head head, Rail... rail){
std::cout << head << ", ";
Print(rail...);
}

int read(){
int x = 0; char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x;
}

int qpow(int a, int b, int m){
if(b == 0) return 1;
int tmp = qpow(a, b / 2, m);
tmp = (tmp * tmp) % m;
if(b & 1) tmp = (tmp * a) % m;
return tmp;
}

const int mod = 998244353;
//维护区间欧拉函数的和,支持区间乘法
struct SegTreeSum{
struct Node{
int lf, rt, sum, lazy;
} t[maxn];
void Pushdown(int now){
if(t[now].lazy > 1){
t[lson(now)].lazy = (t[now].lazy * t[lson(now)].lazy) % mod;
t[rson(now)].lazy = (t[now].lazy * t[rson(now)].lazy) % mod;
t[lson(now)].sum = (t[now].lazy * t[lson(now)].sum) % mod;
t[rson(now)].sum = (t[now].lazy * t[rson(now)].sum) % mod;
t[now].lazy = 1;
}
}
void Pushup(int now){
t[now].sum = (t[lson(now)].sum + t[rson(now)].sum) % mod;
}
void Build(int now, int lf, int rt){
t[now].lf = lf;
t[now].rt = rt;
if(lf == rt){
t[now].sum = t[now].lazy = 1;
return;
}
t[now].lazy = 1;
int mid = (lf + rt) / 2;
Build(lson(now), lf, mid);
Build(rson(now), mid + 1, rt);
Pushup(now);
}
void Update(int now, int lf, int rt, int k){
if(t[now].lf >= lf && t[now].rt <= rt){
t[now].sum = (t[now].sum * k) % mod;
t[now].lazy = (t[now].lazy * k) % mod;
return;
}
if(t[now].lf > rt || t[now].rt < lf){
return;
}
Pushdown(now);
Update(lson(now), lf, rt, k);
Update(rson(now), lf, rt, k);
Pushup(now);
}
int Query(int now, int lf, int rt){
if(t[now].lf >= lf && t[now].rt <= rt){
return t[now].sum;
}
if(t[now].lf > rt || t[now].rt < lf){
return 0;
}
Pushdown(now);
return (Query(lson(now), lf, rt) + Query(rson(now), lf, rt)) % mod;
}
} smt;
//计算每个质因子的出现次数最大值和最小值,判断是否要暴力更新
struct SegTreeRM{
struct Node{
int lf, rt, minn, maxx, lazy;
} t[maxn];
void Pushup(int now){
t[now].maxx = max(t[lson(now)].maxx, t[rson(now)].maxx);
t[now].minn = min(t[lson(now)].minn, t[rson(now)].minn);
}
void Pushdown(int now){
if(t[now].lazy){
t[lson(now)].lazy += t[now].lazy; t[rson(now)].lazy += t[now].lazy;
t[lson(now)].maxx += t[now].lazy; t[rson(now)].maxx += t[now].lazy;
t[lson(now)].minn += t[now].lazy; t[rson(now)].minn += t[now].lazy;
t[now].lazy = 0;
}
}
void Build(int now, int lf, int rt){
t[now].lf = lf;
t[now].rt = rt;
if(lf == rt){
t[now].maxx = t[now].minn = t[now].lazy = 0;
return;
}
int mid = (lf + rt) / 2;
Build(lson(now), lf, mid);
Build(rson(now), mid + 1, rt);
Pushup(now);
}
void Update(int now, int lf, int rt, int p, int c){
if(t[now].lf >= lf && t[now].rt <= rt){
if(t[now].maxx == 0){
t[now].maxx += c; t[now].minn += c; t[now].lazy += c;
smt.Update(1, t[now].lf, t[now].rt, ((p - 1) * qpow(p, c - 1, mod)) % mod);
}
else if(t[now].minn > 0){
t[now].maxx += c; t[now].minn += c; t[now].lazy += c;
smt.Update(1, t[now].lf, t[now].rt, qpow(p, c, mod));
}
else{
Pushdown(now);
Update(lson(now), lf, rt, p, c);
Update(rson(now), lf, rt, p, c);
Pushup(now);
}
return;
}
if(t[now].lf > rt || t[now].rt < lf){
return;
}
Pushdown(now);
Update(lson(now), lf, rt, p, c);
Update(rson(now), lf, rt, p, c);
Pushup(now);
}
} tr[30];

int n, m, xi[maxn];
bool check(int val){
if(val == 1) return false;
for(int i = 2; i * i <= val; i++)
if(val % i == 0) return false;
return true;
}
int cnt, pri[maxn];
void initp(int lim){
for(int i = 1; i <= lim; i++)
if(check(i)) pri[++cnt] = i;
smt.Build(1, 1, n);
for(int i = 1; i <= cnt; i++) tr[i].Build(1, 1, n);
}
void De(int l, int r, int w){
for(int i = 1; i <= cnt && w > 1; i++){
int p = pri[i], c = 0;
while(w % pri[i] == 0){
w /= pri[i];
c++;
}
if(c) tr[i].Update(1, l, r, p, c);
}
}
signed main(void)
{
n = read(); m = read(); initp(100);
for(int i = 1; i <= n; i++) xi[i] = read();
for(int i = 1; i <= n; i++) De(i, i, xi[i]);
for(int i = 1; i <= m; i++)
{
int ty = read();
if(ty == 0){
int l = read(), r = read(), w = read();
De(l, r, w);
}
else{
int l = read(), r = read();
printf("%lld\n", smt.Query(1, l, r));
}
}
return 0;
}
❌