Codeforces Global Round 2 Solution

时间:2023-03-08 15:56:12

这场题目设置有点问题啊,难度:Div.2 A->Div.2 B->Div.2 D->Div.2 C->Div.2 D->Div.1 D-> Div.1 E->Div.1 F简直有毒

只AC 4题似乎就是1000+名了

这种考验手速的时刻Itst就比较擅长了,然后就红名+拿衣服了……

A. Ilya and a Colorful Walk

如果最左边和最右边不同就是\(N-1\),否则就是中间跟两边颜色不同的块到两边距离的最大值

代码

B. Alyona and a Narrow Fridge

二分答案,对于一个二分值\(mid\)将所有要放的牛奶拿出来,每一次最高的和次高的放在一层

代码

C. Ramesses and Corner Inversion

注意到对任意\(x \times y\)的矩形做一次操作,等价于对这个矩形内所有\(2 \times 2\)的子矩形做一次操作,于是我们只需要对\(2 \times 2\)的矩形做操作就可以了,这个直接从左往右、从上往下、要翻转就翻转就可以了

代码

D. Frets On Fire

题目相当于要求:有\(n\)条线段,左端点为\(s_i\);\(q\)组询问,每一组询问每条线段的长度都为\(l\),问线段覆盖总长度

设\(s_{i+1} = INF\),那么答案就是\(\sum\limits_{i=1}^n \min\{s_{i+1} - s_i , l\}\)。把所有\(s_{i+1}-s_i\)存下来排个序,每一次询问时二分

代码

E. Pavel and Triangles

显然每一次选出的三角形的边长一定是\((2^i,2^i,2^j)\),其中\(j \leq i\)。所以可以从小往大贪心,每一次选择尽可能多的三角形,即先把\(< i\)的剩下的木棒尽可能地用掉,然后再3个3个地取当前剩余的木棒

代码

F. Niyaz and Small Degrees

先考虑对于一个\(x\)求答案。考虑树形DP。

设\(val_u\)表示\(u\)到它父亲的边的权值,设\(f_{u , flag = 0/1}\)表示对于\(u\)子树内的点除\(u\)以外度数都不超过\(x\),且\(u\)的度数不超过\(x+flag\)时的最小代价。

转移考虑:对于一个点\(v\),假设它的度数为\(d_v(d_v > x)\),那么转移到\(f_{v,1}\)的状态中需要切掉\(v\)与其孩子的\(d_v - x - 1\)条边,而转移到\(f_{v,0}\)需要切掉\(d_v - x\)条边。而对于\(y \in ch_v\),切掉边\((y,v)\)意味着\(f_v\)从\(f_{y,1}+val_y\)转移,否则从\(f_{y,0}\)转移。所以初始默认答案为\(\sum\limits_{y \in ch_v} f_{y,0}\),对于所有孩子按照\(f_{y,1}+val_y-f_{y,0}\)从小到大排序,选择前面若干条边切掉进行转移。

然后可以发现:\(\sum\limits_{i=1}^n d_i = 2(n-1)\)即\(\sum\limits_{i=0}^{n-1}\sum\limits_{j=1}^n [d_i > j] = 2(n-1)\)。所以如果称某一次询问中必须要切掉相邻的若干条边的点为重要点,则在所有询问中,重要点的总和为\(2(n-1)\)。

到这里不难想到虚树。对于每一次询问建立虚树。因为虚树上无法体现重要点与非重要点之间的边,但是实际有可能会切掉这样的边,所以对于每一个重要点用一个堆维护它和它非重要点孩子之间的边的边权。DP过程中,如果某个点\(x\)与虚树上\(x\)的孩子\(y\)在原树上的距离超过\(2\),则直接用\(\min(f_{y,0} , f_{y,1} + val_y)\)转移\(f_x\),否则像上面一样先默认转移\(f_{y,0}\),然后把\(f_{y,1} + val_y - f_{y,0}\)丢进堆里,用堆求前若干小转移到\(f_{x,0}\)和\(f_{x,1}\)。

将询问从小到大做,就可以直接维护每个重要点的非重要点儿子的堆。

Update:关于priority_queue,它的赋值操作似乎跟元素个数有关(但是非常快?),大力赋值在菊花图上会TLE,所以要用multiset对操作进行撤销。

代码

G. Get Ready for the Battle

答案的下界是\(\lceil \frac{\sum\limits_{i=1}^m hp_i}{n} \rceil\),而构造题一般都会取到这个下界(要不然怎么构造啊喂),所以考虑构造一种方式使得总攻击次数达到下界。那么我们需要尽可能少地浪费兵力,也就是说要尽可能把所有敌人打到\(0\)血。

为了构造的方便,我们令攻击方式为:先所有军队一起攻击第一个敌人,当第一个敌人的血量\(<n\)时,让一个前缀的军队攻击第一个敌人,让第一个敌人恰好达到\(0\)血,剩下的军队直接去攻击第二个敌人。维护前缀和\(sum_i\),也就是说对于\(\forall i \in[1,m)\),要存在一个前缀的军队,它们的人数之和为\(sum_i \mod n\)。不难想到一种可行方案:将所有\(sum_i \mod n\)排序得到数组\(b_i\),\(b_0 = 0 , b_m = n\),则\(s_ i = b_i - b_{i-1}\)。

构造方案直接暴力模拟上面的攻击方法。

代码

H. Triple

一个暴力是\(O(n2^kk)\)的FWT,显然跑不过

显然地,对于三元组\(A,B,C\),将其变为\(0,A \oplus B , A \oplus C\),把最后结果的下标异或上\(A\),这两者得到的结果等价。

那么在初始的数组中,\(F_0 = a , F_{A \oplus B} = b , F_{A \oplus C} = c\),FWT之后每个位置一定会是\(a+b+c,a+b-c,a-b+c,a-b-c\)中的一个。

设暴力FWT之后全部乘起来得到的某个位置的值为\((a+b+c)^x(a+b-c)^y(a-b+c)^z(a-b-c)^w\),解出\(x,y,z,w\)就可求出FWT结果。

首先\(x+y+z+w=n\),然后任意给\((a,b,c)\)赋值不会改变\(x,y,z,w\)的值,那么令\((a,b,c) = (0,1,0)\),将得到的\(F\)数组加起来FWT。因为FWT的和等于和的FWT,故有\((a+b+c)x + (a+b-c)y + (a-b+c)z + (a-b-c)w = p\),其中\(p\)就是FWT后得到的当前位置的值。对于\((a,b,c) = (0,0,1)\)也做一遍。

现在有了\(3\)个方程,但\((a,b,c)=(1,0,0)(0,1,0)(0,0,1)\)都出现过了,再取\((a,b,c)\)都和这三个向量线性相关,所以要换一种思路。考虑数组\(G\),\(G[x] = \sum\limits_{i=1}^n [B_i \oplus C_i = x]\),对这个数组FWT,可以得到\(x-y-z+w=q\),其中\(q\)是FWT完成之后的点值表示。

原因大概是:设序列\(F\)FWT后的序列为\(F'\),那么\(F[b]\)对\(F'[a]\)的贡献的系数是\((-1)^{count(a \& b)}\),其中\(count(x) = x\)在二进制下\(1\)的个数,这个可以由FWT的式子得到。如果第\(i\)个三元组FWT之后第\(j\)位的值为\(a-b+c\),那么有\(2 \not\mid count((A \oplus B) \& j) , 2 \mid count((A \oplus C) \& j)\),即\(2 \not\mid count((B \oplus C) \& j)\),那么\(F[B \oplus C]\)对\(F'[j]\)的贡献就是\(-1\),其余同理。

那么现在得到了\(4\)个方程组,可以求出点值表示,最后IFWT一下。

代码

BONUS:可以利用与上面类似的方法解决更一般的对于\(m\)元组、\(A,B,C < 2^k\)的问题,复杂度约为\(O(2^{m+k}(m+k)+nm)\)。