人生巅峰,这是我第一次 \(\textrm{AK div2}\)!
一场 \(\textrm{div2}\) 让我信心大增,感谢中国同胞!!!
Link \(\textrm{to Codeforces}\)。
Contest
A. Subset Mex
Legend
给定长度为 \(n\ (1 \le n \le 100)\) 的数组 \(a\ (0 \le a_i \le 100)\),把它们分成两个集合,使得 \(\operatorname{mex}(S_1)+\operatorname{mex}(S_2)\) 尽量大,可以为空。
数据组数 \(1 \le t \le 100\)。
Editorial
显然贪心构造即可。
Code
可以但没必要。
B. Maximum Product
Legend
给定长度为 \(n\ (5 \le n,\sum n \le 10^5)\) 的数组 \(a\ ( |a_i| \le 3 \times 10^3)\)。从中选 \(5\) 个数字,使得乘积尽可能大。
数据组数 \(1 \le t \le 2\cdot10^4\)。
Editorial
显然排完序只能选两端的。
Code
可以但没必要。
C. Link Cut Centroids
Legend
给定一棵树,共 \(n\ (3 \le n,\sum n \le 10^5)\) 个节点,请删除一条边再加上一条边,使得图依然联通且树的重心有且仅有一个。
数据组数 \(1 \le t \le 10^4\)。
Editorial
找到重心。
- 如果只有一个,随便断一条边再连上。
- 如果有两个,则它们肯定不是叶子,那么把其中的一个重心任选一个子树接到另一个重心去即可。
Code
int c1 ,c2 ,sz[MX] ,mx[MX] ,n;
void dfs(int x ,int f){
mx[x] = 0;
sz[x] = 1;
for(int i = head[x] ,d ; i ; i = h[i].next){
if((d = h[i].node) == f) continue;
dfs(d ,x);
sz[x] += sz[d];
mx[x] = max(mx[x] ,sz[d]);
}
mx[x] = max(mx[x] ,n - sz[x]);
if(mx[x] == mx[c1]) c2 = x;
if(mx[x] < mx[c1]){
c1 = x;
c2 = 0;
}
}
void solve(){
mx[0] = 114514;
cin >> n;
for(int i = 1 ; i <= n ; ++i) head[i] = 0;
tot = c2 = c1 = 0;
int u ,v;
for(int i = 1 ; i < n ; ++i){
cin >> u >> v;
addedge(u ,v);
}
dfs(1 ,0);
if(!c2){
cout << u << " " << v << endl;
cout << u << " " << v << endl;
}
else{
for(int i = head[c1] ,d ; i ; i = h[i].next){
if((d = h[i].node) != c2){
cout << c1 << " " << d << endl;
cout << d << " " << c2 << endl;
break;
}
}
}
}
D. Three Sequences
Legend
给定长度为 \(n\ (1 \le n \le 10^5)\) 的数组 \(a\ (|a_i| \le 10^9)\)。
同时有另外两个长度与 \(a\) 相同的数组 \(b,c\) 满足:
- \(b_i+c_i=a_i\)。
- \(b\) 单调不降。
- \(c\) 单调不升。
你需要最小化 \(\max(b_i,c_i)\),输出这个结果。
以及还有 \(q\ (1 \le q \le 10^5)\) 次修改,将 \(a\) 区间 \([l,r]\) 加上 \(x\ (|x| \le 10^9)\)。
Editorial
好题!有那么点意思。
因为 \(b\) 单调不降,我们考虑如下式子:
\(b_2=b_1+ \Delta_1\ (\Delta \ge 0)\),\(b_1+c_1=a_1\),\(b_2+c_2=a_2\),\(c_1 \ge c_2\)。
整理可得:\(a_1+\Delta_1 \ge a_2\)。
我们肯定是希望 \(\sum \Delta\) 尽可能小,所以 \(\Delta\) 取到等号最优,即 \(\Delta_i=\max(0,a_{i+1}-a_i)\)。
我们要求的答案是 \(\max(b_n,c_1)\),那我们得先把这个式子表示出来。
根据定义我们可以得到 \(b_n = b_1 + \sum \Delta\)。我们肯定是希望 \(c_1,b_n\) 比较接近,这样才取到最优。
\(c_1=b_1+\sum \Delta\),整理得到 \(c_1 = \left\lceil \dfrac{a_1 + \sum \Delta}{2} \right\rceil\) 即为最终答案。
区间加只会改动差分数组的两个位置,改变了 \(\Delta\) 的值。
Code
LL a[MX] ,del[MX] ,cost ,n;
void upd(int pos ,int v){
if(pos > n || pos == 1) return;
LL before = max(del[pos] ,0LL);
del[pos] += v;
cost += max(del[pos] ,0LL) - before;
}
LL Ans(LL x){
if(x >= 0) return (x + 1) / 2;
return x / 2;
}
void solve(){
cost = 0;
cin >> n;
for(int i = 1 ; i <= n ; ++i){
cin >> a[i];
del[i] = a[i] - a[i - 1];
if(i != 1 && del[i] >= 0) cost += del[i];
}
cout << Ans(a[1] + cost) << endl;
int q; cin >> q;
for(int i = 1 ; i <= q ; ++i){
int l ,r ,d; cin >> l >> r >> d;
if(l == 1) a[1] += d;
else{
upd(l ,d);
}
if(r != n){
upd(r + 1 ,-d);
}
cout << Ans(a[1] + cost) << endl;
}
}
E. Deleting Numbers
Legend
本题为交互题。
你有一个集合 \(\{1,2,\cdots,n\}\ (1 \le n \le 10^5)\),现在有一个数 \(x\ (1 \le x \le n)\),你要猜它。你可以对集合进行如下操作:
- 询问集合中有多少个 \(a\ (1 \le a \le n)\) 的倍数。
- 询问集合中有多少个 \(a\ (1 \le a \le n)\) 的倍数,并把 \(a\) 的倍数删除。特别地,\(x\) 永远不会被删除。
- 告诉交互器答案是 \(a\)。这个操作只能执行一次。
你最多可以操作 \(10^4\) 次。
Editorial
a useful web
这种题目嘛,所有人都会想到质数。于是我马上进了 \(\textrm{number empire}\) 网站(Link),搜索了 \(10^5\) 以内的质数个数,发现很巧,正好有 \(9592\) 个,与题目限制的 \(10^4\) 十分接近。发现到答案有可能是个大质数,因为每一个质数都至少要做一次操作 \(2\),所以询问次数的下界就是 \(9592\)。我们还有 \(408\) 次可以做其他的事情。
a well-known conclusion
用到一个十分基础的结论:\(> \sqrt{n}\) 的质因子最多只有一个。
for prime \(\le \sqrt{n}\)
我马上发现 \(\sqrt{n}=316.227766\cdots\),发现 \(\le \sqrt{n}\) 的质数只有 \(65\) 个。我们不妨对这 \(65\) 个质数进行暴力操作,每次先删了 \(p\) 的倍数,再询问 $p1,p2,p^3\cdots $ 是否还有。发现这最多只需要 \(65+15\) 次就能搞定。(\(15\) 是因为 \(2^{17}>10^5\))。
for prime \(> \sqrt{n}\)
接下来对于 \(> \sqrt{n}\) 的质数还有 \(9528\) 个,但是这当中最多只有一个因子。我分了以下两种情况讨论:
- 这个数存在 \(\le \sqrt{n}\) 的因子。
那么可以对每一个 \(> \sqrt{n}\) 的质数使用操作 \(1\),看元素数量对不对,没对上就说明肯定这个 \(>\sqrt{n}\) 的因子就是当前询问的。
- 这个数不存在 \(< \sqrt{n}\) 的因子。
考虑每 \(\sqrt{9528}=97.611474\cdots\) 个质数分成一块,每删除完一个块中所有数就检查一下 \(1\) 的倍数有多少个(即集合元素数量),如果没对上就说明这个因子一定在这个块里面。暴力检查块内元素即可。最多消耗 \(98+98=196\) 次。
所以最坏情况消耗次数是 \(9592+65+15+98+98=9868\) 次,可以通过。
复杂度我不太会算,如果是枚举 \(1 \to n\),那复杂度是 \(O(n \log n)\) 但只枚举了质数,远远达不到。
**upd: ** 复杂度与埃氏筛是一样的,即 \(O(n \log \log n)\)。
Code
交互题调试还要自己写交互器……
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int MX = 1e5 + 233;
int pri[MX] ,Npri[MX] ,n ,tot;
int vis[MX];
int shouldrem;
void sieve(){
Npri[0] = Npri[1] = true;
for(int i = 2 ; i <= n ; ++i){
if(!Npri[i]) pri[++tot] = i ;
for(int j = 1 ; j <= tot && pri[j] * i <= n ; ++j){
int aim = pri[j] * i;
Npri[aim] = 1;
if(i % pri[j] == 0) break;
}
}
}
/*
int dele[MX] ,Ans;
int del(int x ,int v = 1){
int ret = 0;
for(int i = x ; i <= n ; i += x){
ret += !dele[i];
if(v == 0) continue;
if(i != Ans) dele[i] = 1;
}return ret;
}
*/
int AskB(int x){
cout << "B " << x << endl;
// return del(x);
cin >> x; return x;
}
int AskA(int x){
cout << "A " << x << endl;
// return del(x ,0);
cin >> x; return x;
}
int gar;
int main(){
cin >> n;
// Ans = 1949;
shouldrem = n;
sieve();
int Ans = 1;
int i = 1;
for( ; i <= tot && pri[i] <= min(317 ,n) ; ++i){
int p = pri[i];
gar = AskB(p);
for(int j = p ; j <= n ; j += p)
shouldrem -= !vis[j] ,vis[j] = 1;
int ok = AskA(p);
if(ok) Ans *= pri[i];
for(p *= pri[i] ; ok && p <= n ; p *= pri[i]){
ok = AskA(p);
if(ok) Ans *= pri[i];
}
}
if(Ans != 1){
for( ; i <= tot ; ++i){
gar = AskB(pri[i]);
int tmp = 0;
for(int j = pri[i] ; j <= n ; j += pri[i])
shouldrem -= !vis[j] ,tmp += !vis[j] ,vis[j] = 1;
if(gar != tmp){
Ans *= pri[i];
break;
}
}
}
else{
int cnt = 0;
for( ; i <= tot ; ++i){
++cnt;
AskB(pri[i]);
for(int j = pri[i] ; j <= n ; j += pri[i])
shouldrem -= !vis[j] ,vis[j] = 1;
if(cnt == 98 || i == tot){
gar = AskA(1);
if(gar != shouldrem){
for(int j = 0 ; j < 98 ; ++j){
gar = AskA(pri[i - j]);
if(gar){
Ans *= pri[i - j];
goto out;
}
}
}
cnt = 0;
}
}
}
out:
cout << "C " << Ans << endl;
return 0;
}
Summary
是一场很有趣的 \(\textrm{div 2}\),虽说是中国场但没有什么中国 \(\textrm{OI}\) 的气息(指数据结构、多项式等)。考察的数学知识点较多,感觉 \(\textrm{PJ}\) 选手也能做起来友好的样子。
我自己要反思一点是:\(\rm E\) 其实并不算太难,却卡了我很久,有两点原因:
- 对自己没有信心,高估 \(\textrm{2E}\) 难度,导致我想了半个小时没有想出来就开始慌张。以至于算分块复杂度的时候把质数数量看成了 \(10^5\),均值不等式一分析,询问次数不够……就更加慌张,还好最后发现了问题。
- 对于交互题的不熟练,我测试样例就用了很久时间。交互题的询问什么的应该封装到一个函数里,这样子可以方便自己写交互库自己测试。我因为没有调试导致损失了 \(150\) 分,不然的话就可以到 \(\textrm{rank5}\) 了。