7.23 CF训练题解

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 3

7.

23 CF训练题解
BY 颜智瑶

A. Swaps in Permutation
题意

给一个1~n的排列,再给出m对下标 ,每次操作可以选择一对 并将当前 进行交换。


求交换后的字典序最大的排列。

思路

我们将这个问题具象化成图。对于每对 产生一条连边,则m对下标具象化为由若干个联通块。
可以发现,在同一个联通块中出现的下标,可以通过若干次合法交换,得到任意的排列。

(举例说明)

因此,要让字典序最大,即让同一个联通块中下标所对应的数字降序排列,再在原数组对应位置修
改。采用 或 寻找联通块皆可。

联通块后的排序处理:

①最朴素的思想是分别记录每个联通块中的下标和对应 ,对 降序sort后按下标升序填入对应
位置。

(下标也可以直接计数)

②有一种更加简洁优美的处理方法是优先队列,把同联通块的值都放进祖先对应的优先队列里,遍
历数组时找出该下标祖先对应的队首即可。

   for (int i = 1; i <= n; ++i) {


       f[i] = find(i);
       q[f[i]].push(a[i]);
  }
   for (int i = 1; i <= n; ++i) {
       a[i] = q[f[i]].top();
       q[f[i]].pop();
  }

和7.22晚上的E解法有异曲同工之妙。

B. Sequence Sorting
题意

给一个长为n的数组,每次可以选择一个x,并将数组中所有满足 的数移到数组头或数组
尾,求最少操作几次可以让数组单调不减。

思路

设移到数组头的元素中最大值为 ,移到数组尾的元素种最小值为 。

不难发现,为使数组有序,显然 。我们会:

先将与 相等的移到数组头,再将数组中所有满足 的由大到小移到数组头;


然后将与 相等的移到数组尾,再所有满足 的由小到大移到数组尾。

因此,数组中没有发生移动的是一段连续的区间 。题目所求即这段连续区间长度最大值。

对于一个数 ,记录它第一次出现的位置 和最后一次出现位置 。什么样的数在操作中可以


不被移动?没有“插队”的数,即 < 对于 恒成立。

题目要找一段连续的、没有“插队”的数,可以套用dp的思维。当然,简单变量维护也是可以取代dp
数组的。

我们可以这样处理:对数组排序去重后,获得数组b。遍历数组b,如果发现“插队”数,连续区间清
空,重置成1;否则,就是dp转移,长度+1。dp[i]表示不需要移动的数字,那么对于第 位,ans = 不
重复元素总数-dp[i]。

最后的答案是所有ans里的最小值。

   int ans = n, len = a.size();


   for (int i = len - 1; i >= 0; --i) {
       if (i + 1 == len || r[a[i]] >= l[a[i + 1]]) dp[i] = 1;
       else dp[i] = dp[i + 1] + 1;
       ans = min(ans, len - dp[i]);
  }

(也可以灵活运用变量代替排序和离散化CodeForces1223D Sequence Sorting_邵光亮的博客-


CSDN博客)

C. Coffee Break
题意

有n杯咖啡,每杯咖啡对应a[i]表示我想在每天工作的第几个时刻喝掉它。m是每天的工作时间,d
是两杯咖啡的间隔,求最少几天可以喝完所有咖啡。

思路

贪心解决即可,因为要求按读入顺序输出,所以要先对每杯咖啡标记编号。又因为每杯咖啡都要喝
的,肯定是时刻越早的尽可能往前排,来不及喝的增加一天总天数。所以我们按咖啡饮用时间排序,掏
出我们美丽的优先队列,记录已确定天数的最后一杯咖啡的时刻以及是第几天喝的,尝试在这杯咖啡后
再喝一杯咖啡。如果间隔大于d,则保存答案;否则,增加一天。再把新增的咖啡也放进优先队列,模拟
一遍过程即可。

D. Two Teams
题意

有n个队员排成一队,第i个人的能力值是a[i]。有两个教练在选拔队员,由一队教练优先。教练们每
次会在剩下的队员中挑出能力值最大的,并且把该队员当前左边k个人和右边k个人(不足k个则尽可能
选)也选进这个队伍,问最终每位队员的队伍分配情况。

思路

模拟题。

显然,取出剩下能力值最大只需将原数组排序,按能力值从大到小选人,遇到已经被选(已经记录
答案的)就跳过。唯一需要解决的问题就是如何以i为中心,向两边各取k个,并且将它删除。
一个很好的选择是方便逐个查询及删除的链表结构。采用L、R数组模拟双向链表,初始化后,每次
向左右遍历k个数,并将删除区间的左右结点连接,这样每个队员至多被访问一次,就可以在 的时
间内完成选队员和出队的维护。

       int j1 = idx, j2 = idx, r = 0;  


//j1~j2即这次被选中的区间,r为当前方向已被删除的个数
       while(L[j1] && r++ < k) {  //向左遍历链表
           ans[L[j1]] = coach;
           j1 = L[j1];
      }
       r = 0;
       while(R[j2] && r++ < k) {  //向右遍历链表
           ans[R[j2]] = coach;
           j2 = R[j2];
      }
       R[L[j1]] = R[j2], L[R[j2]] = L[j1];  //删除区间

E. Marcin and Training Camp


题意

有n个队员,第i个人可以用两个值表示:a[i]和b[i]。

有编号为0~59的60种算法,把第i个人的a[i]转换成一个60位二进制,若则二进制第j位为1,说明第i
个人掌握第j种算法,否则为0。当A掌握了一种B不会的算法,他就会觉得自己比B厉害。

现在要选拔若干队员(至少两名),如果有个人觉得自己比队内所有人都厉害,这就是个狂妄的
人,我们不欢迎这种人。

选拔队的能力是所有入选队员的b[i]之和。求选拔队能力最大值,且保证队内没有狂妄的人。

思路

首先,如果有多个人a[i]相同,那么众生平等,谁也不会鄙视谁,一定可以都选拔进来;

其次,如果 且 ,说明 为1的位上 也为1,即 不会认为自己比


强。

看数据范围,n<=7000,可以支持一个 的暴力。也就是说,发现重复出现的a[i],先把 加
入队伍,再向前查找,把所有不觉得自己很厉害的 加入,即把 的 也加入队伍。

   sort(a + 1, a + 1 + n);
   for (int i = 2; i <= n; ++i) {
       if (a[i].first != a[i - 1].first) continue;
       vis[i] = 1;
       for (int j = 1; j < i; ++j) {
           if ((a[i].first & a[j].first) == a[j].first) vis[j] = 1;
      }
  }

You might also like