目录
Educational Codeforces Round 167 (Rated for Div. 2)
Codeforces Round 940 (Div. 2) and CodeCraft-23
Codeforces Round #956 (Div. 2) and ByteRace 2024
等八月份学完知识一定回来补题)
Codeforces Round 953 (Div. 2)
A:n本书编号为1~n,第i本有ai页,分为两堆,每堆中取编号最大的一本,求页数最多能取多少
思路:最后一本必取,找出除最后一本外的最大的页数即可
B:n个包子,可以让前k(任意)个包子的价格为b-i-1,剩下的包子价格都为a,求最多可以卖多少钱
思路:分情况:如果b比a还要小,那么全卖a就是最优解;否则就找出b-i+1和a相等的位置k,k之前用第一种方法卖,k之后用第二种
C:找出一个n的全排列P使得所有的|P1-1|+|P2-2|+|P3-3|+......+|Pn-n|等于k
思路:因为是排列,所以有一个位置a[i] != i那么就一定会有另一个位置a[j] != j
考虑初始数组为1~n
第一种情况:将两个数对换位置后可使得结果增加|i-j|*2;
另一种情况:将某个数插入到前面然后令数列整体后移,结果为|i-j| + 区间长度,仍为|i-j|*2;
可得出如果k为奇数则必定无解,剩下的就是双指针l = 1,r = n从两边开始互换答案累加,注意不要超过k,如果互换之后答案超过了k那么就取消这次互换然后让左右任意一个指针移动一步继续操作,不需要互换的地方a[i]=i
D:n个人参加选举,每人固定投票为a[i],额外有c票会投给当前人中序号最小的,票数相同时序号最小的人获胜;其中可以进行如下操作:去掉i个人,并将他们的票数视为额外票数;求每个人要想拿到冠军最少需要排除几个人
思路:
第一种情况:当前i == 1并且a[i]加上c就是最大的人 或者 当前的a[i]是最大的人并且当前的a[i]大于c+a[0] 则输出“0"(即把额外的票都给第一个人后当前的a[i]仍为全场最大或者i == 1并且a[i]为全场最大)
第二种情况:记数组中最大值为maxnum,若a[i]的值不为最大,则需要将前i-1个人都排除掉才能将额外票加到自己身上,此时若总票数大于maxnum则输出i-1(即不管maxnum的位置在前面还是后面都没有影响),否则需要把maxnum的那一个人也去除掉,输出i(即maxnum肯定在后面且比当前前缀和大)
E:给两个01序列a、b,有如下两种操作:a[i] = a[i+2] = 0则可将b[i+1]置为1;b[i] = b[i+2] = 1则可将a[i+1]置为1;给出n个询问区间求该区间中的a[i]最多有多少个1
思路:b[i]可被a[i-1]和a[i+1]影响,a[i]可被b[i-1]和b[i+1]影响,也就是a[i]最多能影响到a[i-2]和a[i+2];
即询问区间[l,r]中[l+2,r-2]的答案是和整体一样的;
可以先求出原整体序列的新序列nb[i]和na[i],然后对其求前缀和;
b[i]可以被a[i]中的0影响,而a[i]中的0不能被b[i]影响,所以先让a[i]给b[i]操作,然后b[i]再回去给a[i]操作
每次询问长度小于4直接暴力求,否则求[l+2,r-2]的前缀和然后再对剩余四个端点单独求解即可,求解过程要注意b[i]和nb[i]在什么情况下才能用(即上面所推导的i只能影响长度为2的区间),长度大于4的时候b[l+2]和b[r-2]都可以被改变,所以用nb就是最优
F:后面再补
Codeforces Round 955 (Div. 2)
A:给出两对比分x1:y1和x2:y2求前者变为后者的过程是否可以满足任意时刻x,y不等
思路:显然要使两比分不相等只能让x1,y1中的较大者先变化,然后再看较小者增加后是否又超过了较大者即可
B:给定常数x,y,有k次操作,每次可使x加一,当x能被y整除时x变为x/y,求x最终值
思路:求出y-y%x的值后用k减去这个值,同时让x加上这个值
也就是先让x满足x被y整除然后一直除下去直到x+k小于y或者k为0为止,当x为1的时候x的最终结果为1+k%(y-1),其他情况正常计算即可
C:n张牌顺序抽取,每张牌有序号ai,求有多少个不相交区间满足区间和在[l,r]之间
思路:若a+b满足,a+b+c也满足则最优解可能为a+b和c,主要在于c是否能单独满足
所以按照贪心思路进行队列前缀和操作,一旦满足条件即可记录答案+1同时前缀和清零,前缀和超出范围时就不断让队头出队依次判断是否满足即可
D:给定一个数值矩阵和01矩阵,01代表两种不同的山,数值代表山的海拔,可以操作任意次k*k的矩阵,让该子矩阵数值整体+c,求是否能满足两种山海拔之和相等
思路:根据01分为两种山求出两山全部的海拔之差NUM,然后直接枚举所有的k*k矩阵中的01数量之差,因为每次操作造成的收益都是k*k矩阵中01之差的倍数
求出x个01之差后可以列出等式:A1X1 + A2X2 + A3X3 + ...... = NUM,其中A为未知量
也就是让任意数量的任意X1~Xn之和等于NUM,即使X1~Xn的最大公约数能被NUM整除,根据裴蜀定理求解即可
E:数位dp不熟练后面补
F:给出一个n个元素的数组a,每次修改其中的一个值,求第一次操作之前和每次修改之后要想使a数组不降序排列要排序的最小子区间
思路:
根据题目描述可以把区间分为三部分:[1,L],[L,R],[R,N]
其中1~L和R~N都是数组中满足不降序的最长前后缀,L~R为存在逆序的区间
要使整个数组不降序,则要满足[L,R]中的最小值大于等于前缀中的最大值,[L,R]中的最大值小于等于后缀中的最小值
也就是找到前缀中第一个大于[L,R]中最小值的位置和后缀中第一个大于等于[L,R]最大值的位置,同时要注意后缀中元素小于前缀的可能,所以后缀要找到第一个大于等于max([L,R],a[L-1])的位置
可以用通过维护违反了单调性的位置i,最大的i和最小的i就是区间[L,R],找出该区间的最大最小值再二分即可,可以用很多方法求解,例如set(能排序的)和线段树(能求区间最值的)加二分来实现
Educational Codeforces Round 167 (Rated for Div. 2)
A:给n个硬币坐标(x,y),初始在原点(0,0)处,可以朝八个方向移动,每次移动后硬币纵坐标都会-1,问每个硬币是否有可能被拿到
思路:最坏情况每次都斜着走恰好和硬币下落相遇,其余情况为一部分斜着走剩下一部分竖着走等硬币下落或者不可能拿到,由于我们先动,也就是只需要判断每个硬币的纵坐标是否满足y+1 >= 0即可
B:给出两个字符串,求包含a为字串且包含b为子序列的字符串的最小长度
思路:a,b长度小于等于100,暴力枚举b作为a的子序列的最大字符个数即可
C:n个人,2场电影,每个人对这两场的电影为0、1或-1,现给出这n个人对这两场电影的评价,若安排一个人只看一场电影,求两场电影中评价最低的最大可能值
思路:分情况:
评价两场都为-1,则两场必定有一场评价-1;都为1,则必定有一场+1;都为0则不需要考虑;
一场评价为0,另一场评价为1则给另一场+1,若为-1则选择评价为0的那场;一场-1一场+1则必定考虑+1;
最终可以得到四个变量:ans1(第一场绝对增加的值),ans2(第二场绝对增加的值),sub(两场必定有一场-1的次数,即a[i]=b[i]=-1的情况),add(两场必有一场+1的次数,即a[i]=b[i]=1的情况)
最后分配sub和add给ans1和ans2让其尽量平衡输出较小值即可
D:有n个装备,第i个装备制造需要a[i]个相同类型金属,融化可以获得b[i]个原类型金属,每次制造和融化都能获得1经验,先给出m个不同类型的金属,数量为ci,求最大能获得的经验数
思路:乍一看像是贪心但又感觉复杂度爆炸,后来一想既然ai <= 1e6那么就dp枚举出1e6范围内的所有最优解,大于1e6的时候就用贪心直到其小于1e6即可
贪心:当一个金属ci数量满足ci >= ai的时候,一定是选择ai - bi最小的最划算
用v[i]表示金属数量为i时的最划算的一次锻造+融化所减少的金属数量;
用dp[i]表示金属数量为i时能够锻造+融化的次数
v[i[初始设为正无穷,可得出v[i] = min(v[i],v[i-1])(也就是只有前面金属数量小于最小的ai时是没有办法操作的,剩下的情况只需要找出最小的花费即可)
dp[i]初始为0,可得出dp[i] = max(dp[i],dp[i-v[i]]+1)(即有i个金属时的最优解为i减去当前最优的一次花费后的最优解次数+1,因为v[i]是当前最划算的一次最小花费数)
当ci > max(ai)时,只需要对ci进行( ci-max(ai) ) + 1次v[max(ai)]操作即可让c变为小于max(ai)的数,把前面的收益再加上dp[c]即为所求
最后答案要*2,因为上面表示的都是锻造+融化的次数
E、F:后面再补
Codeforces Round 948 (Div. 2)
A:n次操作每次在顶端放下一个正方形或者取下一个正方体,问n次操作结束后能否满足正方体堆叠高度正好为m
思路:首先只有n >= m的时候才可能有答案,然后还要保证n > m的那一部分为偶数
B:给出一个整数x,求一个长度小于等于32的数组a,要求:ai的值只能为0、1或-1;ai给数组带来的收益为ai * 2^i;不存在ai != 0且ai+1 != 0的位置。构造一个这样的数组使得数组总收益为x
思路:一个int范围内的整数肯定可以用32个以内的2的倍数来构成,所以优先考虑只加(ai = 1)的情况,构造出一个只包含1且收益为x的数组后再根据第三条要求进行判断
首先要知道如果删掉了2^i这个数就代表着总数减去了2*2^(i-1)和2^i并且加上2^(i+1);
所以遇到连续的ai都为1的时候我们可以让a[i-1]变为-1,a[i]变为0同时a[i+1]++;
若a[i] == 2则进位,即a[i] = 0并让a[i+1]++即可
C:给出一个数组a,要求找出一个子序列让该子序列的最小公倍数不在数组a中,求这个序列可能的最大长度
思路:首先我们知道两个数一定是这两个数的最大公倍数的因子;先找到数组a中的最大值maxnum,然后查看是否有不是maxnum的因子的ai,若有则表示整个数组都可取(因为此时最小公倍数已经大于数组中所有数),否则若全是maxnum的因子则进行以下操作:
找出maxnum的所有因子,若该因子在数组中存在则舍去;
用剩下的因子q依次对原数组进行操作,找出原数组中所有能被该因子q整除的数,即该因子的因子;
最后求的这些数的最小公倍数若为q则记录该序列长度,找出长度最大的一组即可;
也就是让maxnum的每个不在数组中的因子作为答案一次次遍历数组来求解(因为此时数组中都是maxnum的因子)
D:给出一个01矩阵,每次操作可使任意一行01取反,求如何操作才能使得每一列只有一个1的列数最多
思路:不难发现,只要确定了其中一列只有一个1就相当于确定了整个矩阵,所以可以枚举每个点,通过操作其他行让此列只有该点为1,此时就是一种可能的答案;
每次确定一个点时都对整个矩阵操作时间复杂度太高,所以考虑用map来记录下每次对所有行操作的01序列(0为不变,1为取反);
可知重复出现次数最多的即为答案(因为出现一次代表有一列为可行答案);
然后就会发现内存超限了,因为存了太多的字符串,所以考虑字符串哈希,找出出现最多的哈希值后再遍历一次原矩阵再一次哈希后若得出的值相等则此操作即为答案
E:交互题后面补的可能性不大
Codeforces Round 941 (Div. 2)
A:手上有n张数量不一的牌,每次可以选k张相同的牌换取k-1张手中有的任意点数的牌(可以不一样),求手中牌最少能有多少张
思路:看是否有牌的数量大于等于k,有的话就可以全换掉,手中最少为k-1,否则为n
B:给定一个只有黑白方块的矩阵,每次可以选择颜色相同两个点,使包含这两个点的最小矩阵全部变为这两个点的颜色,问最终是否能全变为一个颜色
思路:对于矩阵的四个顶点,只需要判断不包含这个顶点的另外两条边上是否有和该点颜色相同的格子,四个点只要有一个满足即可
C:n堆石子,A和BA先出手,两人轮流进行该操作:选择一个数x,要求x不得大于n堆中最小的一堆中石子的数量,然后在每堆中取出x个石子。轮到某人时若不能操作(即没有石子)时该人输
思路:若只有一堆,则先手必胜,所以我们判断如何操作才能达到只剩一堆
假设所有石头数量都不相同,那么每次拿最小的一堆-1块石头,也就是剩一块石头,那么下一个人的操作就会固定且只能拿完最小的一堆
也就是说如果想胜就必须要夺得在大于1的选择中的先手权,也就是将石子排序去重后若第一堆大于1则A获得先手权A胜
否则找到第一个a[i]-a[i-1] != 1的位置,即代表a[i-1]拿完之后先手权在第一个操作ai的人手上,这个位置是奇数则A胜,偶数B胜
最后还要考虑如果差都为1的情况,那么就相当于全都按顺序拿,最后一个拿的人获胜,也就是要判断n是奇数还是偶数,同样奇数A胜偶数B胜
D、E、F:后面补
Codeforces Round 940 (Div. 2) and CodeCraft-23
——那场数学+dp毁了我的cf梦
A:给出n种不相同的木棒,每种数量ai,求最多能组成多少个用相同木棒组成的正多边形
思路:别被样例恰好四根木棒骗了,实际最少的正多边形是三角形,对每部分木棒除三即可
B:给出一个数组长度n和数字K,要你构造一个长度为n的数组,要求数组异或和的二进制下的1最多
思路:要使1最多也就是找出第一个在二进制下全为1且小于等于K的数x,然后剩下的K-x对异或结果没有影响,随便加给另外任意一个数即可,其余输出0
C:给出一个n*n的矩阵,当玩家将棋下在(x,y)时电脑会下在(y,x)当且仅当x!=y,且玩家和电脑都不能在这一行或这一列有棋子的位置上下棋,现给出玩家的前K个操作,求剩下的棋盘还有多少种不同放置结果的可能(颜色不同或位置不同都算不同结果)
思路:题目保证给出的操作和电脑的操作都是有效的,也就是说如果玩家操作在(x,x)上那么电脑不会操作,同时这个矩阵也会少了一条可操作的对角线,变相的变成了一个(n-1)*(n-1)的矩阵
若x!=y那么玩家操作导致少一行一列可操作,电脑操作同样会导致少一行一列可操作,也就是变相的将矩阵变成了(n-2)*(n-2)的矩阵,也就是当输入的K组每组的x!=y那么矩阵大小就-2,否则就-1
考虑大矩阵结果太多不好计算,尝试逆向求解,如果一个大小为n的矩阵多出来一行一列,那么就会有两种情况:
玩家操作在(x,x),则电脑不能操作,只会多出一种情况
玩家操作在(x,y),则电脑操作在(y,x),且玩家可以在多出来的一行或这一列上的出对角线外的任意一个位置操作,也就是2*(n+1) - 2个操作,将这些操作次数乘上矩阵大小为n-1(因为一次操作会少两行两列)时的结果次数再加上大小为n时的操作乘1(因为放对角线才会减少一行一列)就是矩阵大小(n+1)的答案,
可以得出状态转移方程:dp[i] = dp[i-1]*1 + dp[i-2] * (2*i-2),初始化特殊值dp[0] = dp[1] = 1,dp[2] = 3
D:给出一个数组,计f(x,y)为a[x]到a[y]的异或和,求f(x,y)⊕f(y,z)>f(x,z)这样的三元组{x,y,z}的个数
思路:可以看出原式f(x,y)⊕f(y,z)>f(x,z)中把[x,z]分成了[x,y]和[y,z]两个区间,这两个区间异或展开是a[x] ^ a[x+1] ^ ...... ^ a[y] ^ a[y] ^ ....... ^ a[z],相较原式多出了一个^a[y],也就是f(x,z)⊕a[y[>f(x,z)
根据异或的性质,我们知道如果一个数被异或奇数次那么还是原来的数,异或偶数次就会变成0,所有我们可以通过枚举每一个a[y],找到a[y]二进制下的最高位1,如果一个包含a[i]的区间中这个位置出现了奇数次,那么f(x,z)⊕a[y]<f(x,z)否则f(x,z)⊕a[y]>f(x,z)
现在的目标是找出每个a[i]的前面的数在和a[i]二进制下最高位1的相同位置下有多少个奇数1和偶数1,同样也要找到后面的
设pre[0/1][i][j]为第i个数的二进制下第j位的前面有pre[0][i][j]个偶数1,pre[1][i][j]个奇数1,las[0/1][i][j]同理,计算前后的个数乘积就是组合的数量
E、F:后面补
Codeforces Round 939 (Div. 2)
A:给一个递增数组a和一个数组q,要求当有q[i[个人玩游戏时每次把位置为a[i...n]的所有人踢出去,剩下的人重新排序,直到不能踢为止,求每个q[i]最后会剩多少人
思路:很显然,答案就是max[q[i],a[1]-1]
B:有2n个数,数的范围都在1~n,保证每个数正好有两个,现在将这2n个数分配给你和Nene,输入的数组a代表你的数,每一轮中你先将一个数放到桌子上,若桌子上有这个数则得分+1,Nene后手同理,现保证Nene用最优的方式来让你得分最少,求你能得到的最高分是多少
思路:考虑手中数的可能:只有一个或者有两个,如果有两个那就表示我们必定可以得一分,若有一个则我们必定不能得分(因为Nene会选择最优操作),我们有每有两个相同的数Nene也会有另外一个相同的两个数,我们有一个的数Nene手上也会有,又因为我们先手,所以我们只能得到手中两个数的得分
C:给出一个n*n的矩阵,每次操作可以选择一行或者一列,让这一行或一列成为一个1~n的排列,排列中的数字可以任意安排,求最多2n次操作的情况下能得到的最大矩阵和为多少
思路:既然要最大化这个矩阵,那我们就要让最大的数尽量多的同时让最小的数尽量少;不难发现,无论如何操作最后一步都是要将某一行或者某一列覆盖为一个排列,所以我们可以将最后一步的操作放在最外层,假设这一步操作后是最终结果,那么就说明这一步被覆盖掉的数不能大于最后一步的数,也就是说要让1和1去重合才能保证其他的会小于等于,然后倒数第三步和第四步就是保证2和2重合(因为后面会被覆盖).....说起来有些抽象,最终结果是个这样的图:
1 2 3 4 5
2 2 3 4 5
3 3 3 4 5
4 4 4 4 5
5 5 5 5 5
根据上面的解释,我们可以从右下角的五开始操作,一直覆盖下去,也就是让最后一行和最后一列全变成1~5的排列,然后让倒数第二行第二列变成1~4的排列....重复覆盖下去就好了,正好操作2n次
D:给出一个不超过18个数的数组,每次操作可以让一个区间变成这个区间中未包含的的最小正整数,最多操作5e5次求数组最大和是多少
思路:一个不为0的可以通过一次操作变为0,再一次操作变为1,再操作又变为0,长度为2的区间可以通过一系列操作变为两个2,由于数组长度最大为18,我们可以在2^n内枚举所有可能的选择,然后找出所有的连续区间,结果为每个连续区间的长度的平方(一个长度为n的区间可以经过操作变为n个n)再加上不被操作的数之和,记录这些答案的最大值即可,同时也要记录操作次数,每个长度为n的区间可以经过2^n次操作遍为n个n(自己可以试试)简单说下:
想要得到4444就需要得到0123,想要0123就要先得到3333,想要3333就要先得到012X,想要012X就要先得到222X,想要222X就得要01XX....不断的分下去,然后我们发现得到3333后还需要得到0123,也就是变为0003,然后在重复操作...(最后写这部分代码用了六页草稿演算...脑子短路了)
这个2的n次方是算上一次区间推平(整体区间变为0)操作的,但是不保证区间内u没有0,所以遇上要操作的区间中的0单独多记录一次次数就好
E1:n只怪兽,每只怪兽能量值为a[i],第i只怪兽每一轮会攻击第i+1只怪兽(如果没似的话),能量值为y的怪兽被能量为x的怪兽攻击后能量值会变为max(0,y-x),能量若为0则不能攻击,求最后10的100次方次操作后有几只怪物能量不为0
思路:考虑有三只小怪兽的情况,a攻击b,b攻击c,c暂时先受着,若一轮下来b,c都不为0的话那么b收到的伤害是a,c受到的伤害是b-a,看起来好像没什么,但是我们推导一下,若保证b每次都不死的情况下,c收到的伤害会是多少
假设a为1,这是最小可能,让b暂时先为100,b可以撑100回合,那么c最少需要多少?很明显是(99+98+....+2+1),也就是(n+1)*n / 2,达到了n方级别的数,注意这个n目前代表的是b的能量,若从a开始算起,那么b最少需要(n+1)*n / 2,而c要在这个基础上再去求和 ,达到了n的三次方的规模
如果有一个小怪兽的上一个小怪兽的能量为0,那么肯定是可以存活的,而上面推导发现有三个连续的怪兽若都想存活下来,那么第三个怪兽能量至少是第二个的平方倍,是第一个的立方倍(这里二分之一和四分之一的常数暂时忽略),根据题目ai小于1e5,可以暴力模拟500-700次来让有三个连续的怪兽其中至少一个归0,这样操作后原序列就会变成了最多只要两个连续的非0的序列,两个怪兽的情况不用想第二个总会被耗光,所以只需要找出a[i-1]==0的所有位置i即可
E2:这题与上面的区别就在于ai改为了1e9
思路:继续考虑暴力模拟,不过这里就不能取平方规模的数了,会爆掉,所以只能取立方规模的数来让序列最大连续不超过三个,当有a,b,c三只小怪兽的时候,可以推出b最多能撑住a的攻击b/a + b%a != 1次,然后求c最多能坚持多少
c第一次减是减去b-a,第二次是减去b-a-a,第n次是减去b-n*a,可以发现这是一个第一项为b-a,最后一项为b%a(这是被攻击了b/a次之后的b,因为要保证b>0才能攻击c)的等差数列,b最多可以攻击b/a次,因为最后一次被a攻击后归0就没法攻击了
所以c最多会承受长度为b/a、首项为b-a,尾项为b%a,公差为a的等差数列之和,若c大于这个值就说明c能存活,否则c不能存活,同时b肯定是不能存活的,因为有a在
还需要考虑最后一种特殊情况:a[1],a[2],a[n]的时候,这个时候a[1]攻击a[2],a[2]不能攻击,a[n]攻击a[1],不适用于上面的等差数列,重新推一个式子求解或者直接暴力模拟下来都可以,因为只要这一种,时间是够的,然后剩下的操作就和上面基本一样了
F:后面再看
Codeforces Round 945 (Div. 2)
A:规定两人对弈胜者+2,平局两者+1分,败者不扣分,现给出三人的最终得分,求最大平局可能
思路:首先最终总分一定是二的倍数,不是的话输出-1就好,然后由于数量很小,可以手动模拟,每次让当前最大的和最小的平局一次即可
B:给出一个数组a,求找出一个最小的数k能使得数组a中所有长为k的区间所有值的或运算结果相等
思路:首先或运算的结果是满足单调不减的,其次若k满足条件,则k+1必然满足条件
简单证明:假设一个长度为9的数组满足k=3,那么肯定能保证a[1]|a[2]|a[3] == a[4]|a[5]|a[6] == a[7]|a[8]|a[9],同时我们知道两个相同的数进行或运算结果不变,一个大数和一个小数进行或运算若小数在二进制下的1在大数的相应位置上都存在,那么这个大数也会不变,所以无论是a[1]|a[2]|a[3]|a[4]还是a[1]|a[2]|a[3]|a[4]a[5]或者全都异或一遍,最终的结果还是和a[1]|a[2]|a[3]一样
所以可以考虑使用二分,不满足就让二分值增加,满足就减少,最终的结果就是k值
对于二分后求每个区间的或运算结果我们可以用双指针滑动窗口来解决,每次左指针移动的时候判断加上新进来的数是否有二进制某位的1在这个区间中没有,然后再判断丢掉的数是否有这个区间中唯一的某个二进制位置上的1即可
C:给出一个偶数长度的数组a,数组值保证是一个1~n的排列,求找出另一个1~n的排列p让p[i] + a[i] > p[i-1]并且p[i] + a[i] > p[i+1] + a[i+1](n < i < 1)的位置尽可能多
思路:既然要求这样的位置尽可能多那么就操作一下看看能不能满足全是这样的位置,发现这样的位置有两种选法 第一种:3,5,7,9,11..... 第二种:2,4,6,8,10.....
由于n是偶数,那么这两种的数量其实都是一样多的,都是(n/2) - 1个数
接下来考虑对着两种情况进行操作,首先既然是要保证他们比两边的大,那么就尽量把大的数加给他们,既然我们要保证每个数最终都能大于两边的数,那肯定不能一家独大,要按照顺序把大数加给相对小的数,也就是对这两种序列按照递增顺序排列,把前从n开始的(n/2) - 1个数依次加给他们
然后考虑分配剩下的数,剩下的数只要保证他不大于等于两边的最小的那个数即可,每次找出能加上的最大值,从上面操作剩下的数中找是否有这个数,没有就挑出第一个比这个数小的数加上即可,保证这是最优情况,若不能操作则换另一种选法
做了这么多思维题了就应该大胆猜测不是)
D:交互题应该不补了
F、G:后面补
Codeforces Round 932 (Div. 2)
A:给一个字符串和一个大于等于2的偶数n,每次操作可以让字符串翻转或者在字符串后面加上反转后的字符串,求n次操作后字典序最小的字符串
思路:不难发现,操作大于两次之后的操作都是无用或者在原不变的情况下基础上加后缀的操作,所以答案只在原字符串、翻转后的字符串再拼接翻转后再翻转的字符串、原字符串拼接翻转后的字符串再整体翻转这三种情况种
B:给出一个数组,求是否能分成>1段,要求每段中不被包含的最小正整数相同
思路:很明显若能分成k段的话,那么保证这k段中不被包含的最小正整数都是x,也说明了整个数组的不被包含的最小正整数也是x,可以先求出这个x,然后找出一个点让左边的不被包含的最小整数和右边的不被包含的最小正整数都为x即可,因为无论分成多少段都可以合并为两段(因为这些段的结果相同),可以用二分来找出第一个满足1~mid内恰好不被包含的正整数为x的区间,然后判断右区间是否也是x即可
C:有n条消息,每条消息对应两个数组a和b,阅读第pi~pk条消息要花费 的时间,现给出一个时间X,求这个时间内最多能看多少条消息
思路:这题最迷惑人的就是这个pi,注意不要搞混,pi不一定是连续的,但i是连续的,这个式子求的是1~k的求和,也就是说需要构造一个p1~pk让这个式子小于等于X的同时让k尽可能大
首先读明白题后应该能明白式子的第一项没有什么限制,数在哪个位置都一样,受限制的是第二项,要最小化这项式子,也就是让b数组的差距减小;
所以可以先按照b的值进行升序排序,然后就会发现若选择一个区间[l,r],那么这个式子的第二项就是b[r]-b[l],可以枚举所有l和r,然后每次贪心的选择这个区间中最小的a[i],这样能保证k长度最大,具体可以用优先队列或集合等来实现,每次将a[i]选入,判断当前式子总和是否大于X,若大于则删去选中的最大的a[i]再继续操作,记录最大的合法长度即可
列举几个可能不明白的点(用红色表示删掉这个数)假设排序后 某个区间2 3 4 5 6 7 8:
2 3 4 5 6 7 8,2 3 4 5 6 7 8,2 3 4 5 6 7 8,这三种情况时最优答案分别在枚举[l+1,r],[l,r-1],[l+1,r+1]的时候记录到,不用担心出错
2 3 4 5 6 7 8最迷惑的一种可能,再理解一下pi数组的含义,这种情况p1=1,p2=4,p3=6,p4=8;
D:给出一个集合,再给出一个数C,求x+y不在集合中且y-x也不在集合中的个数,其中x,y属于[0,C]
思路:比C简单,可以求出所有的可能再减去上面那两种可能即可
首先0~C能组合的数有(c+1) * (c+2) / 2种(共c+1个数)
对于一个属于集合中的数n,满足x+y等于n的个数有(0,n),(1,n-1)...共n/2 + 1种;满足y-x等于n的个数有(0,n),(1,n+1)...(c-n,c)共c - n + 1种
对于y-x 和 x+y都在集合中的可能:设n,m都在集合中,x+y=n,y-x=m,那么可以推出x = (n+m)/2,y = (n-m)/2,即n和m奇偶相同时x,y才能是整数,否则不满足,所以只需要记录奇数的个数和偶数的个数,最后分别求出这些奇偶相同的数有多少种两两组合即可
E、F:后面补
Codeforces Round #956 (Div. 2) and ByteRace 2024
A:构造一个长度为n的序列,要求i的倍数上的位置数之和能被i整除
思路:很明显i的倍数之和一定能被i整除,直接输出1~n即可
A代码 (这里输出了i*2,只要第i个数是i的倍数即可)
B:给出一个只包含0,1,2的矩阵a和b,每次找出a的一个子矩阵,让其中一对对角顶点+1 % 3,另一对+2 % 3,问能否通过变换得到b
思路:(听说这题是结论题?)赛时手搓了一遍全用2*2暴力改的方法结果发现可行,然后就过了......
简单解释一下:
0 0 0 1 2 0 1 0 2
0 0 0 经过变换可以得到 2 1 0 再给右边变换一下可以得到 2 0 1
0 0 1 2 1 2
0 0 2 1 0 0
0 0 同理先变换上面 0 0 在变换下面 2 1,也就是说如果变换是合法的那么一定能由2*2变换来
C:给出三个数组,数组长度相同,数组总和sum相同但是每个位置上的数不一定相同,找出l1,l2,l3,l4,l5,l6使得a[l1~l2],b[l3~l4],c[l5~l6] 全部满足 >= sum/3(向上取整),且三个区间互不相交
思路:六种情况,这里用先后顺序代表区间所在位置:abc,acb,bac,bca,cab,cba分别模拟一遍即可
D:给出两个数组,每次选择l,r和p,q要求r-l == q-p,交换a[l],a[r]和b[p],b[q],求是否能让a,b相等
思路:首先判断一下a和b中的数是不是完全一样,只有一样的时候才有可能
然后分析操作,由于长度不一样的时候操作起来没什么规律,考虑每次只交换相邻的两个数,这样操作次数如果为偶数的话可以保证a数组不变,也就是让b通过交换相邻的两个数来变为a
简单证明:一个长度为n的区间交换第一项和第n项如果通过相邻交换的方法来操作那么就会操作(n-1)+(n-2)次,保证次数是奇数,如果需要在a中交换一个长度为n的区间,可以把这个操作转换为在b中操作,既然a[l]和a[r]交换结束后a和b相等那么也就是说当前的a[l] = b[r],a[r] = b[l],所以一次操作就相当于两次b中的操作,此时(n-1)+(n-2)*2和2的奇偶性相同,都为偶数,所以a中操作偶数次相邻数组互换结果不变
让b数组的数据变成a数组中的顺序需要多少次相邻的交换,是不是很熟悉,就是逆序对的定义,求出逆序对的个数然后判断是偶数还是奇数即可
E、F、G:人数断档了,后面补
Codeforces Round 931 (Div. 2)
A:给定一个数组,找出4个下标要求ai-aj,aj-ak,ak-al,al-ai的各个绝对值之和最大
思路:很明显最大和最小的就是差距最大的,直接排序后选择前两个和后两个即可,由于求的是差距,所以a1和an的差距加上a2和an-1的差距是等价于a1和an-1的差距加上a2-an的差距的
B:求一个数用1,3,6,10,15最少需要几个数才能组成
思路:一个数很大的时候肯定是优先拿15最优,等到一定范围内再分情况讨论,由于情况太多所以用dp来求,dp[i]表示数为i时的最小选择数,这个范围最好是要大于等于两倍的这六个数的最小公倍数,也就是只需要求dp的前60项就好了(实际好像四十就可以了?)这里不给出严谨证明,因为时间空间都很富裕,求出100000项都不是问题,大于100000的肯定用15是最优这个就不解释了
最后求出来需要多少个15和剩下的dp[剩余值]即可
C:交互题,跳了
D1:给出两个数n和m,每次n可以找一个数y,其中0<y<n并且0<n⊕y<n,让n = y或者n = n⊕y,求n是否能通过63次以内的操作得到m
思路:这个63给的多少有点诈骗了,肯定不少人以为如果有解的话可以按照一位一位变换得来,实际上只有两种情况,当然不排除2次可以完成的任务63次也能完成
第一种:n⊕m小于n,直接可以让n=m
第二种:n⊕m大于n,也就说明了m二进制下最高位的1的位置在n中为0,因为异或是相同为0不同为1,所以我们看看能否找到另一个大于m的且n⊕x小于n的数x,也就是让n中的这一位变为1而且还要满足n减小
令m的最高位的1位置为p,n的最高位的1的位置为q,那么我们要找的数x如果存在那么只能在p~q之间,而且还要保证q不是p~q之间的n中的唯一的1,如果q是唯一的1的情况下既要n⊕x大于m还要n⊕x小于n,这是不可能的,因为x的第q为是1,第p为也是1,已经大于了n,所以如果有解的话就必须要n在p~q之间其他的位置k上有1,让这个第k位为1和第p位为1的数x去进行n⊕x运算,这样不但n会减小而且第p位上也有了1,这样就可以进行第一种操作了
有点乱,总结一下就是m的二进制下的最高位1的位置p,若n的第p位也是1就可以直接一步出结果(因为n⊕m<n),否则就需要在n中找到n的最高位1的位置和m的最高位1的位置之间的另一个1,然这个位置和p位都为1的这个数x去和n做n⊕x操作,用得到的结果再去⊕m即可
D2:还是交互题,跳了,这里给个思路,偶数当n的二进制表示中1的个数为偶数时先手必胜,否则必败,因为二进制的1有偶数个时可以按照题目分为两个二进制为奇数的数
E:后面补
Codeforces Round 930 (Div. 2)
A:定义一个操作:在一个长度为n的数组中,从1开始,对于下标为k(k > 2)的数交换a[k]与a[d],其中d是k的最大除数(d != k),求最后1的位置
思路:从最开始模拟一下,会发现1第一次是和2交换,那么后面的数想要和1交换只能是2的最小倍数的位置,也就是4,后面同理,因为一个数的最小大于1的倍数是2,所以找到n中最后一个2的次方的位置即可
B:有一个二行的01矩阵,每次只能向下或者向右走,求从(1,1)走到(2,n)所经过的字典序最小的01串以及有多少种方式能走出这个01串
思路:很明显如果向下走了之和就没有办法进行别的操作,只有一种向右的操作可以执行,向下走的情况只可能是当前位置向下的位置为0并且右边的位置为1(因为要满足字典序最小)或者下面右面的数一样,可以通过比较每个位置的右边和下边来选择最小的01串,如果相同就说明都可以走,可以正着找到第一个必须向下走的位置,然后从这个位置开始倒着找到第一个必须向上走的位置,这两个位置的差就是得到最小01串的次数,如果没有这两个位置就说明可以随便走,次数有n种
C:交互题,跳了
D:给出一个字符串,字符串只包含‘>’和'<',若当前位置为'>',则下一秒会向右行动,若当前位置为'<‘,则下一秒会向左行动,每次行动会反转当前字符,求查询1~n以每个位置为起点时需要多少时间才能走出格子(每次查询互不影响)
思路:
随便造一个字符串来模拟一下:<>><><>>
假设从第四个位置开始,每次走的距离为(4-3)→3,(6-3)→6,(6-2)→2,(8-2)→8,8+1→走出
不难发现,这其中被减了两次的都是4左边的’>'的位置,加了两次的是4右边的'<‘的位置
剩下的还有一个4本身和字符串长度8,因为只有左边的’>‘会阻止向左走,右边的'<'会阻止向右走
可以找出从当前位置出发能遇到左边的哪些’>'和右边的哪些'<',然后计算答案即可,由于查询次数为n,需要快速查询,所以可以用前缀和分别记录‘>' '<'的个数,再记录一下每个'<'和'>'距离原点的前缀和,因为需要求一个区间内的'>'的位置之和以及'<'的位置之和
每次查询判断左边'>'和右边'<'的数量,哪边小就会走到那边出界,二分一下大的一边的位置,两边相等时就可以走出边界了,记得考虑当前位置先往哪边走,然后再判断左右的数量,按照上面模拟的思路推出四个公式即可,这里不一一解释
核心公式,其余的都是考虑边界条件分情况+ i + n + 1、- i + n + 1、+ i 和 - i
E、F:后面再补
吐槽一下这场赛时写出来三题就能100多名了好恐怖...(我什么时候才能这么强啊.jpg)
Codeforces Round 912 (Div. 2)
A:给出一个乱序数组,可以任意次翻转不大于k长度的子数组,问是否能变成不降序排列
思路:冒泡怎么排的来着,相邻一个个交换就能排好序,所以只要k >= 2就能满足
B:给出一个n*n的矩阵,计M(i,j) = a[i] | a[j],求是否有这样的一个数组a能满足这个矩阵,保证这个矩阵是合法的,即M(i,j) = M(j,i)且M(i,i) = 0
思路:初始让a[i]为2的30次方-1,即二进制下全为1,然后让a[i]与所有的M(i,j)进行&运算,即可得出满足的二进制下1最多的a[i],计算出所有的a[i]再从头判断一遍a[i]|a[j] 和M(i,j)即可
(赛时不知道怎么回事想的a[i]&a[j] <= a[i]然后就用min去求a[i]了结果最后改了四十分钟)
C:给出一个数组,可以把他分成任意连续段(不能丢掉数组中的任意元素),记第j段的字数组之和为sumj,则总答案为,要求最大化这个答案
思路:考虑在某个位置i的时候,如果后面的负数总和为S,正数总和为A,在i前面的位置每多分一段,则后面的答案就会多累加S+A,若到i这里分成了k段,则后面造成的答案为(k+1)*S + (k+1)*A = (k+1)*(S+A)
所以可以从头开始遍历每个位置,考虑从当前位置分出一段是否会带来收益,让当前位置的值为a[i],前面未分段的前缀和为pre,前面已经分了k段,总未分段的值为A+S,若从i后面分出一段,则为分段的值变为(k+1)*(a[i]+pre) + (A+S-(a[i]+pre)) * (k+2)若此值大于之前的(A+S) * (k+1)那么就说明从此段分开答案更优,记录所有最优分段的答案即可
说起来抽象,看代码可能更好理解点
D1:有一个长度为n的数组a和q次询问,每次询问一个操作数k,每次操作可以将任意数组值加一,求最后数组从1到n的&操作结果
思路:题目中n*q <= 1e5,所有可以考虑n*q的做法或者n*q*logn的做法,很明显已经可以暴力到不能再暴力了,k<=1e18,二进制最多为60位,每次贪心从最高位开始看看k能不能满足让所有的数二进制情况下都加到这一位为1,如果能就继续加,最后输出&操作结果即可,开一个新数组每次复制一遍a然后操作即可
D2:后面补
E:交互题,跳了
F:后面补
Codeforces Round 915 (Div. 2)
A:给定一个n*m的矩阵,初始全为摧毁,当且仅当(i-1,j) (i+1,j) 和 (i,j-1) (i,j+1)至少都有一个被重建时当前(i,j)才能免费被重建(重建具有传递性),求初始至少需要重建多少个地方才能让整个矩阵都可被重建
思路:开始考虑重建任意一条对角线上的点,会发现每多一个点就能多重建一圈,也就是x个点能重建x*x个地方,所以矩阵中n,m最大的值即为答案(小的话会少建|n-m|行/列)
B:给你一棵树,每次你可以选择两个结点,让这两个结点之间的路径合并为一个结点,同时两个结点也将合并为一个结点,问最少操作多少次可将这棵树化为一个顶点
思路:每次可以合并一条最长的链,也就是看这棵树有几条链,也就是叶子结点的数量
C:给出一个字符串,找出一个字典序最大的子序列,然后可以让这个子串在原序列中循环右移(序列中其他元素位置不变,只有子序列的相对位置改变),求最少移动几次能让整个序列单调不减或者不可能实现
思路:因为是字符串,而且字符只在a~z之间,所以可以循环26*n次来找到一个最大的子序列(从z开始到a每次找有没有当前的字母,而且位置要在上次找到的字母前面),记录下这个子序列在原序列中的每个位置后就可以开始模拟了,先将子序列排序,然后在将排序后的子序列放回原序列,最后检查是否满足单调不减,循环的次数很明显就是子序列的长度减去子序列中最大的字母出现的次数(因为这个字母不会被循环右移到左边去,只要移走他后面的元素即可)
D:给出一个0~n-1的随机排列,可以循环位移,数组的总收益为mex(i)的前缀和,其中mex(i)为前i个数中没出现过的最小正整数,求循环位移任意次数后最大的收益
思路:因为给出的数是0~n-1的排列,所以mex(i)其实就是后面n-i个数的最小值,这一点可以用单调队列或者线段树/st表等来维护,然后考虑循环位移,可以用很经典的断环的方式把原数组在后面复制一份,这样就有了所有循环的可能结果,然后用长度为n的滑动窗口来求解即可
这应该是大部分都能想到的思路,核心在于这个滑动窗口的值如何改变,模拟一下将一个数放在最后就会发现,如果前面n-1个数的上一次的mex(i)比当前的数要大,那么这次肯定就变为了这个数(因为前面不再存在这个数),然后上次的总和再减去循环位移过来的数的上一次的mex()值就是当前窗口内的总值
使用双端队列来存储一段长为n的mex()值,每次出掉back的元素,从前面开始查看是否有大于等于此次循环位移的值a[i]的元素,有的话就出队将其替换为a[i],很明显这样每次可能要操作n次,所以可以将双端队列维护的值改为两个,一个存储实际的值,另一个存储这个值连续的个数,这样就可以在最多2*O(n)的时间内完成所有的操作,总体复杂度取决于求区间最小值的方法是O(n)还是O(nlogn),两种都可以通过,这里用的是线段树的方法
注意点:当取到全部元素时的收益为n,不是n-1(排列为0~n)
E、F:后面补
Codeforces Round 960 (Div. 2)
A:给出一个数组,Alice先手,bob后手,每次操作可以找出一个大于等于mx的数,让mx变为这个数,然后让这个数变为0,若不能操作则当前操作者失败,问Alice是否有获胜方法
思路:第一眼看过去肯定是拿最大的,判断最大的是偶数还是奇数,奇数则Alice胜,但是偶数Alice也是可以获胜的,因为可以先拿次大的,然后再看次大的是偶数还是奇数...数据不打从50~1挨个循环判断即可
B:给出一个数组长度n和x,y,定义x为数组的前缀和最大时的最小的下标位置,y为数组最大后缀和时的最大下标位置,数组中元素只能为1,-1,请构造这个数组
思路:题目保证x > y,因为[1,x]是整个数组的最大前缀和,所以要保证[x+1,n]这段区间中的后缀和任何时刻不能超过0,因为一旦大于0就说明有更大的前缀和,所以从x+1开始到n构造一段-1,1,-1...的序列,同理[y,n]是最大的后缀和,所以前面[1,y-1]也不允许出现任意时刻前缀和大于0,同理也是从y-1开始到1构造一段-1,1,-1......的序列,最后满足x的最大前缀和的位置、y是最大后缀和的位置,将[x,y]全置为1即可(最后满足0 >= [1,y-1] , [x+1,n] >= -1,[x,y] > 1)
C:给出一个数组a,定义sum为a数组之和(每次操作后更新),每次进行如下操作:ans += sum;更新数组b[i]为前i个数中出现两次及以上的最大的数(若没有则为0)再让a[i] = b[i],求a数组全为0时ans的值
思路:用cnt[i]来表示第a[i]个数出现的次数,模拟第一次操作后数组就会变为0...a...b...c...d...这样的序列(...表示有x个相同的数,a,b,c,d严格递增),不难发现这个数组的后续变化:如果当前第i个数不为0且[1,i-1]中没有该数,那么下一次一定会变为前面出现过两次及以上的最大数或0,设前面最大的数为num(可能为0),那么当前位置的总收益为a[i]+第一次操作后的b[i] + mx * (n-i);因为只要有两个连续的相同数那么这两个数就会不断的传递下去
模拟两种数据:
2 1 1 2 3 3 → 0 0 1 2 2 3 → 0 0 0 0 2 2 → 0 0 0 0 0 2 → 0 0 0 0 0 0
1 2 1 1 2 3 3 3 → 0 0 1 1 2 2 3 3 → 0 0 0 1 1 2 2 3 → 0 0 0 0 1 1 2 2 → 0 0 0 0 0 1 1 2
→ 0 0 0 0 0 0 1 1 → 0 0 0 0 0 0 0 1 → 0 0 0 0 0 0 0 0
D:有一个n*n的网格,每个网格中前a[i]个网格是黑色,后面a[i]+1~n为白色,每次可以让一个2*2的网格染为白色或者将一行网格染成白色,求让网格变为白色的最小次数
思路:(最简单的一题?)很明显2*2可以影响两行,但是不一定效率比直接染一行高,可以分情况讨论,从第一行开始,如果当前这一行主动用2*2变换超过一次(被上一行的2*2操作影响到的不算),那么效率就不如直接染一行(因为最多两行单独染色最多需要2次),若当前行主动用一次2*2可以变为白色那么就选择2*2即可(因为能影响到下一行),每次记录当前行执行2*2染色后的位置pos,由上面可知pos只能为0,2或4,因为当pos为2时,下一行从3开始计算染色即可,若主动染一次2*2可以完成整行染色那么pos继续+2变为4,当pos为4时则改行要处理1~2的格子和5~a[i]的格子,即a[i] > 4则需要染2*2两次以上,此时不如整行染色划算,所以进行整行染色,pos置为0,把所有情况列出来判断即可
E、F:后面补
Codeforces Round 961 (Div. 2)
A:给你一个n*n的矩阵和k个筹码,求筹码最少要填满几条对角线
思路:根据n*n矩阵的特性,长度为n的对角线有一条,n-1有两条...依次填满计数即可
B1:有n种花,每个花的花瓣数为ai,价格也为ai,现在有k个金币,要求买到的花中花瓣数量之差不超过1,求最多能买到的花瓣总数
思路:排序然后双指针记录答案,每次合法的情况下右指针移动然后更新当前区间的总和,注意判断区间总和超过k、右端点的价格减去左端点的价格大于1这些情况即可
B2:和B1大致相同,只不过现在第i种花有ci朵,而且所有的ai互不相等
思路:看到ai互不相等后这题就很好做了,因为相差不能大于1,所以最多只能买两种花,先对ai进行排序,然后依次枚举相邻的两种花,若ai相差大于1则直接跳过,否则进行贪心选择,优先二分小的花的数量k1,然后二分大的花的数量k2,最后考虑能否用剩下的较大的花来替换小的花,也就是加上c1 - k1(小的剩余量)、c2 - k2(大的剩余量)和m - sum(保证不超过m)三者的最小值(因为两者相差1,所以数量差就是总和差)
记得要先记录一遍只买一种花的结果(因为可能所有的差距都大于1)
C:给出一个无序序列,每次操作可以将一个数ai变为ai*ai,求最少需要多少次才能使序列不降
思路:如果是单纯的暴力模拟那么数据肯定会爆,所以可以考虑记录每个位置不小于前一个位置时的操作次数,设a,b为序列中连续的两个数,a通过x次操作使得a比上一个数大,也就是,假设b要通过y次操作才能使得b比x次操作后的a大,也就是
,即有一个数c = y-x使得
>= a,这个数c加上a的操作次数x即为b需要的操作次数,若b本身比a大则反向操作减去这个c(注意小于0时要变为0,因为操作数不可能为负数)
D、E:后面补