1935.Codeforces Round 932 (Div. 2) - sol
20240306
逊哎~
打一半去写申请书然后 12 点睡觉,相对成功!
第二天早上起来把赛时发愣的 C 和 F 切了。
A. Entertainment in MAC
Congratulations, you have been accepted to the Master's Assistance Center! However, you were extremely bored in class and got tired of doing nothing, so you came up with a game for yourself.
You are given a string \(s\) and an even integer \(n\). There are two types of operations that you can apply to it:
- Add the reversed string \(s\) to the end of the string \(s\) (for example, if $s = $ cpm, then after applying the operation $s = $ cpmmpc).
- Reverse the current string \(s\) (for example, if $s = $ cpm, then after applying the operation $s = $ mpc).
It is required to determine the lexicographically smallest\(^{\dagger}\) string that can be obtained after applying exactly \(n\) operations. Note that you can apply operations of different types in any order, but you must apply exactly \(n\) operations in total.
\(^{\dagger}\)A string \(a\) is lexicographically smaller than a string \(b\) if and only if one of the following holds:
- \(a\) is a prefix of \(b\), but \(a \ne b\);
- in the first position where \(a\) and \(b\) differ, the string \(a\) has a letter that appears earlier in the alphabet than the corresponding letter in \(b\).
\(1 \le |s| \le 100\)。
比较一下 \(s\) 小还是翻转的 \(s\) 小。代码。
B. Informatics in MAC
In the Master's Assistance Center, Nyam-Nyam was given a homework assignment in informatics.
There is an array \(a\) of length \(n\), and you want to divide it into \(k \gt 1\) subsegments\(^{\dagger}\) in such a way that the \(\operatorname{MEX} ^{\ddagger}\) on each subsegment is equal to the same integer.
Help Nyam-Nyam find any suitable division, or determine that it does not exist.
\(^{\dagger}\)A division of an array into \(k\) subsegments is defined as \(k\) pairs of integers \((l_1, r_1), (l_2, r_2), \ldots, (l_k, r_k)\) such that \(l_i \le r_i\) and for each \(1 \le j \le k - 1\), \(l_{j + 1} = r_j + 1\), and also \(l_1 = 1\) and \(r_k = n\). These pairs represent the subsegments themselves.
\(^{\ddagger}\operatorname{MEX}\) of an array is the smallest non-negative integer that does not belong to the array.
For example:
- \(\operatorname{MEX}\) of the array \([2, 2, 1]\) is \(0\), because \(0\) does not belong to the array.
- \(\operatorname{MEX}\) of the array \([3, 1, 0, 1]\) is \(2\), because \(0\) and \(1\) belong to the array, but \(2\) does not.
- \(\operatorname{MEX}\) of the array \([0, 3, 1, 2]\) is \(4\), because \(0\), \(1\), \(2\), and \(3\) belong to the array, but \(4\) does not.
\(1 \le n \le 10^5\)。
考虑每一段的 MEX 一定是全局的 MEX,这是比较好想到的。
于是就做完了,从左往右扫一遍,如果当前段的 MEX 已经是全局的了,就直接断开成一段即可。
最后没匹配完的合并到前面一段,判断一下段数是否为 \(1\) 就做完了。代码。
C. Messenger in MAC
In the new messenger for the students of the Master's Assistance Center, Keftemerum, an update is planned, in which developers want to optimize the set of messages shown to the user. There are a total of \(n\) messages. Each message is characterized by two integers \(a_i\) and \(b_i\). The time spent reading the set of messages with numbers \(p_1, p_2, \ldots, p_k\) (\(1 \le p_i \le n\), all \(p_i\) are distinct) is calculated by the formula:
\[\Large \sum_{i=1}^{k} a_{p_i} + \sum_{i=1}^{k - 1} |b_{p_i} - b_{p_{i+1}}| \]Note that the time to read a set of messages consisting of one message with number \(p_1\) is equal to \(a_{p_1}\). Also, the time to read an empty set of messages is considered to be \(0\).
The user can determine the time \(l\) that he is willing to spend in the messenger. The messenger must inform the user of the maximum possible size of the set of messages, the reading time of which does not exceed \(l\). Note that the maximum size of the set of messages can be equal to \(0\).
The developers of the popular messenger failed to implement this function, so they asked you to solve this problem.
\(1 \le n \le 2000\)。
赛时忘记比较大小直接寄。
首先,假设我们选出来了一些数,怎么让它们的代价最小呢?
容易发现,排列对于 \(a\) 的贡献是没有影响的,而对于 \(b\),容易发现我们直接按照 \(b\) 排序去选取,那么这一部分的贡献是 \(\max b_{p_i}- \min b_{p_i}\),而这一定是最优的。
现在来考虑怎么选集合呢?
首先按照 \(b\) 从大到小排个序,那么我们希望的其实就是枚举出 \(\max b\) 和 \(\min b\),算出答案即可。
而这里面的答案其实就是 \(a\) 从小到大去选取,但直接暴力做是 \(O(n^3)\) 的,怎么办捏?
发现假设我们枚举到当前的 \(\max\),不断往前面扩展区间,选取更小的 \(\min\),
看似需要用 priority_queue 不断加入删除找到最优,这样的时间复杂度是不优的。
但是细想一下发现,我们要求是取最大值,如果当前换成的这个 \(\min\) 是不满足条件的,也就是已选的和 \(\gt L\),
我们其实不用管它了,因为这样的答案一定不会取到最大值,上一个才有可能最大。
所以这样做了之后,我们就可以保证复杂度了,
只需要在每次换 \(\min\) 的时候讨论上一个的 \(a\) 选不选即可,同样贪心比较就可以了。
实现比较简单,时间复杂度 \(O(n^2 \log n)\),具体看代码。代码。
D. Exam in MAC
The Master's Assistance Center has announced an entrance exam, which consists of the following.
The candidate is given a set \(s\) of size \(n\) and some strange integer \(c\). For this set, it is needed to calculate the number of pairs of integers \((x, y)\) such that \(0 \leq x \leq y \leq c\), \(x + y\) is not contained in the set \(s\), and also \(y - x\) is not contained in the set \(s\).
Your friend wants to enter the Center. Help him pass the exam!
\(1 \le n \le 3 \times 10^5\)。
感觉做 A 的时间都比做 D 的时间长。
莫名奇妙地想到了容斥——挺典的吧。
首先,一共有 \(\frac{n(n+1)}{2}\) 对可能的 \((x,y)\),而对于每一个 \(s_i\) 我们可以得到以下式子:
- \(x+y=s_i\) 的对数——
- 当 \(s_i\) 是奇数时,为 \(\frac{s_i+1}{2}\),在不考虑 \(x \le y\) 的情况下,有 \(s_i+1\) 中可能。
- 当 \(s_i\) 是偶数时,要特别处理 \(x=y\) 的情况,有 \(\frac{s_i}{2}+1\) 种。
- \(y-x=s_i\) 的对数是 \(c-s_i+1\),非常容易。
减去这两部分,根据简单的容斥原理,我们发现需要加上 \(x+y,y-x\) 都在集合中的对数。
而由于 \((x+y)-(y-x)=2x\),所以对于任意两个奇偶性相同的元素就唯一对应了一对 \((x,y)\),
于是对于每一个 \(s_i\) 加上 \(\ge s_i\) 并且与其奇偶性相同的数即可。
非常简单地就做完了。代码。
E. Distance Learning Courses in MAC
The New Year has arrived in the Master's Assistance Center, which means it's time to introduce a new feature!
Now students are given distance learning courses, with a total of \(n\) courses available. For the \(i\)-th distance learning course, a student can receive a grade ranging from \(x_i\) to \(y_i\).
However, not all courses may be available to each student. Specifically, the \(j\)-th student is only given courses with numbers from \(l_j\) to \(r_j\), meaning the distance learning courses with numbers \(l_j, l_j + 1, \ldots, r_j\).
The creators of the distance learning courses have decided to determine the final grade in a special way. Let the \(j\)-th student receive grades \(c_{l_j}, c_{l_j + 1}, \ldots, c_{r_j}\) for their distance learning courses. Then their final grade will be equal to \(c_{l_j}\) \(|\) \(c_{l_j + 1}\) \(|\) \(\ldots\) \(|\) \(c_{r_j}\), where \(|\) denotes the bitwise OR operation.
Since the chatbot for solving distance learning courses is broken, the students have asked for your help. For each of the \(q\) students, tell them the maximum final grade they can achieve.
\(1 \le n \le 2 \times 10^5\)。
有一个点没想到。
分析样例,首先对于 \(x,y\) 的相同前缀,那么这样的选择是一定的,那么可变的其实就是后面的那一段。
我们假设上界为 \((1010)_2\),如果我们要求第四位一定为 \(1\),那么只有可能是 \((1010)_2\) 和 \((1001)_2\),
反之,只要这里的 \(1\) 我们不取,那么取 \((111)_2\) 一定是最优的。
再回到整体,贪心思路,一定是从高到低能为 \(1\) 就为 \(1\),
如果一个位置上有了两个 \(1\),并且一个 \(1\) 是可变的(相当于可选可不选),
那么这一位后面就全是 \(1\) 了,因为我们可以不选可变的那个 \(1\),然后选后面的所有。
发现这一定是最优的,如果没有,我们就直接用 \(r_i\) 即可,
而哪些可变的 \(1\) 的位置我们可以处理出来,通过与和或即可知道哪些位是满足条件的。
稍加思考发现这些东西都可以用线段树维护,输出的时候在吧可变的 \(1\) 以下的位都变成 \(1\) 就可以了,
这样就做完了,具体可以看代码。代码。
F. Andrey's Tree
Master Andrey loves trees\(^{\dagger}\) very much, so he has a tree consisting of \(n\) vertices.
But it's not that simple. Master Timofey decided to steal one vertex from the tree. If Timofey stole vertex \(v\) from the tree, then vertex \(v\) and all edges with one end at vertex \(v\) are removed from the tree, while the numbers of other vertices remain unchanged. To prevent Andrey from getting upset, Timofey decided to make the resulting graph a tree again. To do this, he can add edges between any vertices \(a\) and \(b\), but when adding such an edge, he must pay \(|a - b|\) coins to the Master's Assistance Center.
Note that the resulting tree does not contain vertex \(v\).
Timofey has not yet decided which vertex \(v\) he will remove from the tree, so he wants to know for each vertex \(1 \leq v \leq n\), the minimum number of coins needed to be spent to make the graph a tree again after removing vertex \(v\), as well as which edges need to be added.
\(^{\dagger}\)A tree is an undirected connected graph without cycles.
\(5 \le n \le 2\times 10^5\)。
上午自己做出来了,写了一篇认真的题解,真挺简单的。
首先,贪心地想,对于每一次连边,从 \(i\) 连到 \(i+1\) 一定是非常优秀的,于是我们尽量都是选这样的边,那么得到的结果是 \(w=m\),一定是最优的。
而并不是任何时刻我们都可以得到 \(w=m\) 的结果的,比如这下面两种:
红色点的答案,\(w=2,m=1\),并不能做到 \(w=m\)。
这是为什么呢?
容易发现,假设一个点 \(u\) 有 \(d\) 条出边,那么把 \(u\) 删除的时候,图就变成了 \(d\) 个连动块,而我们希望再一次把它变成一棵树,相当于把每一个连动块看作一个点,也就是需要 \(d-1\) 条边,那么 \(m=d-1\) 是一定成立的。
而在连接这 \(m\) 条边中,我们尽量是用 \(i \to i+1\) 的边,直到不能连的时候(即所有的 \(i\) 和 \(i+1\) 都在同一个连动块内部),像图中这种情况,我们就需要从 \(u-1\) 连到 \(u+1\)(它们两个一定不在同一个连动块内,可以画图理解),此时的 \(w=m+1\)。容易发现,这是一种合法且最优的方案。
题目中需要我们构造,那么我们现在的问题就是如何找到并用上 \(i \to i+1\) 的边。
以下的分析默认在以 \(u\) 为根的子树中进行,而其所有子树的答案已经计算过了。
对于每一条 \(i \to i+1\) 的边,有两类:
- 从 \(u\) 的子树连到 \(u\) 的子树。
- 从 \(u\) 的子树连到 \(u\) 子树外面。
对于第一种情况,容易发现这两个点 \(i,i+1\) 的 LCA 一定是 \(u\),于是我们可以提前用 vector 存下 LCA 为 \(u\) 的相邻点对。
对于第二种情况,这两个点 \(i\) 和 \(i+1\) 的 LCA 一定是在 \(u\) 的祖先中,而这个祖先越早越好,因为这样它就不会那么早被淘汰(我们的 dfs 是从下到上统计的),直接用一个数组记录一下就可以了。
想到这里,大概就可以写了。
LCA 是可以预处理出来的,你怎么求都可以,笔者写得是用 dfn 序 \(O(n \log n) - O(1)\) 的 LCA。
是否在连动块内的判断可以用并查集完成,实际实现的时候你可以假设 \(u\) 节点的并查集就代表 \(u\) 子树外面的那个连动块,这样就可以边统计答案边更新子树信息,方便以后的 dfs。
每次的构造方案主要就是上面的两种情况,如果存在这样的一条边,并且它所在的两个连动块不连通,那么就直接加入答案就可以了。
最后如果少一条边,就直接加入 \(u-1\) 到 \(u+1\) 的边就可以了,注意这里还需要用并查集合并一下。
感觉没有什么实现难度,由于 \(i,i+1\) 这样的点对只会有 \(n-1\) 对,枚举总次数是 \(O(n)\) 的,所以总的时间复杂度是 \(\mathcal O(n + n\log n)\),瓶颈在于求 LCA。
似乎不用任何数据结构,只要会求 LCA 和并查集就可以了。代码。
int T,n,dep[N],f[N];
vector<int> G[N],H[N];//H 数组记录 LCA 为 u 的 (i,i+1),也就是第一类边
struct Answer{
int w,m;
vector<pii> g;
void init(){w=m=0,g.clear();}
}ans[N];
struct node{
int u,v,d;//d 记录 LCA 的 dep
node(int U=0,int V=0,int D=0){u=U,v=V,d=D;}
bool operator <(const node &rhs)const {return d<rhs.d;}
}c[N];//c 数组维护每一个点和子树外的节点连接的最优点对,LCA 在越上面越优秀
void init(){for(int i=1;i<=n;i++) G[i].clear(),H[i].clear(),ans[i].init(),c[i]=node(0,0,n+1),f[i]=i;}
void add(int u,int v){G[u].pb(v),G[v].pb(u);}
namespace Tr{//dfn 序求 LCA
int idx=0,dfn[N],lg[N],st[21][N];
void dfs(int u,int fa){
dfn[u]=++idx,st[0][idx]=fa,dep[u]=dep[fa]+1;
for(auto v:G[u]) if(v!=fa) dfs(v,u);
}
int Min(int u,int v){return dfn[u]<dfn[v]?u:v;}
void init(){
idx=0,dfs(1,0);lg[1]=0;
for(int i=2;i<=n;i++) lg[i]=lg[i/2]+1;
for(int i=1;i<=lg[n]+1;i++)
for(int j=1;j+(1<<i)-1<=n;j++)
st[i][j]=Min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
int LCA(int u,int v){
if(u==v) return u;
u=dfn[u],v=dfn[v];
if(u>v) swap(u,v);
int s=lg[v-u];
return Min(st[s][u+1],st[s][v-(1<<s)+1]);
}
}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void merge(int u,int v){//并查集
u=find(u),v=find(v);
if(u!=v) f[u]=v;
}
void dfs(int u,int fa){
ans[u].m=ans[u].w=G[u].size()-1;
for(auto v:G[u]) if(v!=fa){
dfs(v,u),c[u]=min(c[u],c[v]);
if(c[v].d<dep[u]) merge(v,u),ans[u].g.pb({c[v].u,c[v].v});//可能存在的第二类边
}
for(auto i:H[u]) if(i!=u&&i+1!=u&&find(i)!=find(i+1)) merge(i,i+1),ans[u].g.pb({i,i+1});//连第一类边
if((int)ans[u].g.size()<ans[u].m){//(u-1,u+1) 的边
++ans[u].w;ans[u].g.pb({u-1,u+1});
for(auto v:G[u]) if(v!=fa) merge(v,u);
}
}
void sol(){
cin>>n;init();
for(int i=1,u,v;i<n;i++) cin>>u>>v,add(u,v);
Tr::init();
for(int i=1;i<=n;i++){//预处理出 (i,i+1) 点对
if(i>1) c[i]=min(c[i],node(i,i-1,dep[Tr::LCA(i,i-1)]));
if(i<n) c[i]=min(c[i],node(i,i+1,dep[Tr::LCA(i,i+1)])),H[Tr::LCA(i,i+1)].pb(i);
}
dfs(1,0);
for(int i=1;i<=n;i++){
cout<<ans[i].w<<' '<<ans[i].m<<'\n';
for(auto j:ans[i].g) cout<<j.fi<<' '<<j.se<<'\n';
cout<<'\n';
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>T;
while(T--) sol();
return 0;
}
目前 CF 最短解,比较神秘。
Conclusion
-
二进制上下界问题可以讨论这一位是否确定了为 \(1\)。(E)
-
加变量的时候注意自定义的函数里面要赋值,不要变成这个样子:(E)
sgt(int A=0,int B=0,int C=0,int D=0){mx=A,d=B,s=C;}//没有 w=D 啊啊啊!
-
贪心地选数的题目一定注意——新加入一个数的时候是不是更优,从而替换掉前面的数!(C)