首页 > 其他分享 >Solution - Atcoder ARC114F Permutation Division

Solution - Atcoder ARC114F Permutation Division

时间:2024-07-28 19:50:32浏览次数:11  
标签:最大化 Division lcp LCP Atcoder val 开头 Permutation operatorname

令 \(a\) 为题目中的 \(P\)。

首先考虑后手的重排策略是什么。
因为 \(a\) 是个排列,元素互不相同,那么肯定就是按照每一段的第一个数的大小,越大的放在越前面。

那么当 \(a_1\le k\) 的时候,显然先手会把以 \(1\sim k\) 开头来划分段。
因为否则存在一个开头 \(> k\) 的段,后手把其放在最前面,那么此时得到的新排列肯定开头 \(> k\);一定不比上面提到的划分方式(开头为 \(k\))优。

接下来考虑 \(a_1 > k\) 的时候。
类似的,可以知道肯定不会有划分出的段的开头 \(> a_1\),因为这样肯定不如就让 \(a_1\) 开头。
所以说每段开头都 \(\le a_1\)。

那么考虑 \(a_1\) 所在的段 \([1, w]\),同时肯定就会有一个以 \(w + 1\) 开头的段。
按照后手的重排策略,最终得到的排列 \(a'\) 肯定满足 \(a_{[1, w]} = a'_{[1, w]}, a'_{w + 1}\ge a_{w + 1}\)。

于是能够发现要使的最后得到的 \(a'\) 尽量小,就需要最大化 \(\operatorname{LCP}(a, a')\),其次如果最大化了 \(\operatorname{LCP} = w\),就需要最小化 \([w + 1, n]\) 选出的段的开头按从大到小的顺序后的字典序。

先来考虑最大化 \(\operatorname{LCP}\)。

考虑枚举 \(\operatorname{LCP}\) 中的最后一段,记开头为 \(i\)。
考虑如果选出了 \(a_{j_1} > a_{j_2} > \cdots > a_{j_m}(1 = j_1 > j_2 > \cdots > j_m = i)\),那么以 \(j_{1\sim k}\) 都成为段的开头,一定是不会影响最终的顺序的。
那么就还需要在 \([i + 1, n]\) 确定出 \(k - m\) 个开头,因为要最大化 \(\operatorname{LCP}\),所以显然会去选最后的 \(k - m\) 个 \(< a_i\) 的数(当然需要满足这些数的位置 \(> i\))。

于是就可以知道需要最大化这个 \(m\)。
那么对 \(a\) 跑一个以 \(a_1\) 开头的最长下降子序列即可求出对于每个 \(i\) 的 \(m\)。

接下来考虑最小化 \([w + 1, n]\) 选出的段的开头按从大到小的顺序后的字典序。
根据上文提到的,可以知道,\(w + 1\) 后选择的开头就是 \(k - m\) 个 \(< a_i\) 的数。
又因为最大化了 \(\operatorname{LCP}\),所以此时最后肯定会更好剩下 \(k - m\) 个数,所以说这 \(k - m\) 个数一定是 \([w + 1, n]\) 中最小的 \(k - m\) 个数。
那么最小化 \(k - m\) 就可以了。

对于上述找到第 \(k - m\) 个 \(< a_i\) 的数,可以考虑树状数组二分得到。

知道开头后,按照后手的方案构造即可。

时间复杂度 \(\mathcal{O}(n\log n)\)。

#include<bits/stdc++.h>
const int maxn = 2e5 + 10, limn = 1 << 18;
int a[maxn], w[maxn];
int f[maxn], val[maxn];
int ed[maxn];
int tr[limn + 1];
inline void add(int x) {
   for (int i = x; i <= limn; i += i & -i)
      tr[i]++;
}
inline int qry(int k) {
   int now = limn;
   for (int i = 17; ~ i; i--)
      if (k > tr[now - (1 << i)])
         k -= tr[now - (1 << i)];
      else
         now -= 1 << i;
   return now;
}
int main() {
   int n, k; scanf("%d%d", &n, &k);
   for (int i = 1; i <= n; i++)
      scanf("%d", &a[i]), w[a[i]] = i;
   ed[n + 1] = 1;
   if (a[1] <= k) {
      for (int i = 1; i <= k; i++)
         ed[w[i]] = 1;
      for (int i = k; i; i--) {
         printf("%d ", i);
         for (int j = w[i] + 1; ! ed[j]; j++)
            printf("%d ", a[j]);
      }
      return 0;
   }
   int mx = 0;
   for (int i = 1; i <= n; i++) {
      if (a[i] > a[1])
         continue;
      f[i] = std::upper_bound(val + 1, val + n + 1, a[i], std::greater<int>()) - val;
      val[f[i]] = a[i];
      mx = std::max(mx, f[i]);
   }
   if (mx >= k) {
      for (int i = 1; i <= n; i++)
         printf("%d ", a[i]);
      return 0;
   }
   int lcp = 0, cnt = 0;
   for (int i = 1; i <= a[1]; i++) {
      int p = w[i], c = k - f[p];
      if (c < i) {
         int q = qry(i - c);
         if (q > p) {
            if (q > lcp) lcp = q, cnt = k + 1;
            if (q == lcp && c < cnt) cnt = c;
         }
      }
      add(p);
   }
   for (int i = 1; i < lcp; i++)
      printf("%d ", a[i]);
   std::vector<int> b;
   for (int i = lcp; i <= n; i++)
      b.push_back(i);
   std::sort(b.begin(), b.end(), [&](int x, int y) {return a[x] < a[y];});
   for (int i = 0; i < cnt; i++)
      ed[b[i]] = 1;
   for (int i = cnt - 1; ~ i; i--) {
      printf("%d ", a[b[i]]);
      for (int j = b[i] + 1; ! ed[j]; j++)
         printf("%d ",a[j]);
   }
   return 0;
}

标签:最大化,Division,lcp,LCP,Atcoder,val,开头,Permutation,operatorname
From: https://www.cnblogs.com/rizynvu/p/18326162

相关文章

  • AtCoder Beginner Contest 363 题解 A-D(待补充)
    A-PilingUp1.1思路其实就是向上取百位的整,需要增加多少,123则为200-123=177;1.2代码voidsolve(){intn;cin>>n;intt=n/100;cout<<(t+1)*100-n;}B-JapaneseCursedDoll 2.1思路就是判断最少需要多少天,会有大于等于P个人......
  • AtCoder Beginner Contest 363
    比赛地址添加链接描述A-PilingUp算法:模拟题目大意在AtCoder竞赛平台中,用户的等级通过正整数分数表示,并根据分数显示相应数量的^符号。分数与^符号显示的规则如下:当分数在1到99(包括99)之间时,显示一个^。当分数在100到199(包括199)之间时,显示两个^。......
  • AtCoder Beginner Contest 364 补题记录(A~F)
    VP五十八分钟苏童流体。好耶A#defineGLIBCXX_DEBUG#include<iostream>#include<cstring>#include<cstdio>#defineintlonglongconstintN=500100;std::strings[N];signedmain(){intn,cnt=1;scanf("%lld",&n);f......
  • AtCoder Beginner Contest 364
    A-GluttonTakahashi(abc364A)题目大意给定\(n\)个字符串,问是否有两个相邻的sweet。解题思路遍历判断当前字符串与上一个字符串是否都为sweet即可。神奇的代码#include<bits/stdc++.h>usingnamespacestd;usingLL=longlong;intmain(void){ios::sync_......
  • AtCoder Beginner Contest 364 复盘
    AtCoderBeginnerContest364当你发现你的做法假了时,再看看题目的时限和空限,你就有可能发现,你的做法真了。本场口胡出了\(5\)题的正解,但是只写出了\(3\)题。弱弱又智智。A-GluttonTakahashi&B-GridWalk&C-MinimumGlutton签到D-K-thNearest算口胡出半......
  • AtCoder Beginner Contest 362
    题目链接:AtCoderBeginnerContest362A.BuyaPentag:签到B.RightTriangletag:模拟C.RightTriangletag:贪心Description:给定\(n\)对整数\(l_i,r_i\);求一个整数序列,满足\(l_i<=x_i<=r_i\),且\(\sum_{i}^{n}x_i=0\);如果没有则输出\(-1\)。Solution:先判断是否有解,令......
  • AtCoder Beginner Contest 363
    上周去玩了(逃A-PilingUp(abc363A)题目大意给定分数,问晋级还差多少分。分别到\(100,200,300\)分能晋级。解题思路找到第一个大于当前分数的,其差即为答案。神奇的代码#include<bits/stdc++.h>usingnamespacestd;usingLL=longlong;intmain(void){io......
  • CF932C Permutation Cycle
    题目传送门思路:个人认为构造题全靠感性理解,理解对了问题就迎刃而解了。(有点像做阅读理解)我们先感性地理解题目中所有变量和函数的含义。\(f\)函数是什么?当\(j\neq0\)时,\(f(i,j)=f(p_i,j-1)\),就是使\(j\)花费了\(1\)的代价,然后使\(i\)变为了\(p_i\)。这就......
  • AtCoder Beginner Contest 360 题解(C-E)
    C-MoveIt题目链接:C-MoveIt题目大意:有\(N\)个盒子和\(N\)个物品编号为\(1-N\),物品\(i\)在盒子\(A_i\)中,重量为\(W_i\),你可以进行一种操作将盒子中的一件物品移动到其他任意盒子中,代价为\(W_i\),求使得所有盒子中只存在一件物品的最小操作代价。题解:贪心,可以发现......
  • [atcoder utpc2023_p] Priority Queue 3
    PriorityQueue3题意:有一个小根堆和\(1\)~\(n\)个数,以及一个操作序列,+表示\(push\),-表示\(pop\),\(pop\)有\(m\)次,问你有多少种插入顺序使得最后的pop集合与给出的的数字集合\(Y\)相同。首先有个浅显的发现:对于不在\(Y\)集合中的数,可选范围形如一个阶梯,换句话......