首页 > 其他分享 >Solution - Atcoder UTPC2023P Priority Queue 3

Solution - Atcoder UTPC2023P Priority Queue 3

时间:2024-07-29 20:40:23浏览次数:12  
标签:Atcoder int Priority 元素 pop operatorname UTPC2023P maxn ll

考虑找一些关于合法的 \(X\) 加入的数的判定条件。

假设遇到了一个 \(\operatorname{pop}\) 操作,令这里删除的数为 \(a_i\),显然 \(X\) 中的数应该要有 \(a_i\),其次 \(X\) 中其他的加入的数要么 \(> a_i\) 要么是 \(a\) 中的元素(在前面的 \(\operatorname{pop}\) 就已经被删了)。

于是可以知道,令最大的一个还没有被 \(\operatorname{pop}\) 出去的 \(a\) 中元素为 \(a_j\),\(X\) 中的元素要么 \(> a_j\) 要么是 \(a\) 中元素。

接下来就有个想法就是直接 DP。
考虑令 \(f_{i, j}\) 为前 \(i\) 个条件,最大的一个还没有被 \(\operatorname{pop}\) 出去的 \(a\) 中元素为 \(a_j\) 的方案数。

但是因为还有上文提到的,可能 \(X\) 加入的数之中还有 \(a\) 中的 \(a_k < a_j\),不是很好处理了。

于是考虑再加一维 \(k\)。
\(f_{i, j, k}\) 表示前 \(i\) 个条件,最大的一个还没有被 \(\operatorname{pop}\) 出去的 \(a\) 中元素为 \(a_j\),当前 \(X\) 中 \(a\) 的元素的个数有 \(k\) 个的方案数。

好像看着就差不多了。
但是考虑到 \(\operatorname{pop}\) 的时候,有可能在 \(k = 1\) 的时候 \(a\) 中 \(\operatorname{pop}\) 的数其实不是 \(a_j\),即 \(a_j\) 没有在 \(X\) 中。

于是再加一维 \(0 / 1\)。
\(f_{i, j, k, 0 / 1}\) 表示前 \(i\) 个条件,最大的一个还没有被 \(\operatorname{pop}\) 出去的 \(a\) 中元素为 \(a_j\),当前 \(X\) 中 \(a\) 的元素的个数有 \(k\) 个,\(a_j\) 是否在 \(X\) 中的方案数。

接下来考虑转移。
令前 \(i - 1\) 个操作有 \(x\) 个 \(\operatorname{push}\) 操作和 \(y\) 个 \(\operatorname{pop}\) 操作。

若第 \(i\) 个操作为 \(\operatorname{push}\):

  • \(\operatorname{push}\) 了一个不在 \(a\) 中的元素。
    这个元素有 \(\operatorname{cnt} = (n - a_j) - (m - j) - (x - y - k)\) 个选择(\(n - a_j\) 为 \(> a_j\) 的数量,后面的为已经选了的 \(> a_j\) 的数量)。
    那么当 \(\operatorname{cnt} > 0, x - y - k\ge 0\) 时,有 \(f_{i - 1, j, k, p}\times cnt \to f_{i, j, k, p}\)。
  • \(\operatorname{push}\) 了一个 \(a\) 中的非 \(a_j\) 元素。
    考虑把这个元素是什么的贡献推迟到后面再算,即现在在这个位置放一个“占位符”,后面再来填补这个地方。
    \(f_{i - 1, j, k, p}\to f_{i - 1, j, k + 1, p}\)。
  • \(\operatorname{push}\) 了 \(a_j\)。
    那么 \(X\) 就相当于从没有 \(a_j\) 到了有 \(a_j\)。
    \(f_{i - 1, j, k, 0}\to f_{i, j, k + 1, 1}\)。

若第 \(i\) 个操作为 \(\operatorname{pop}\):

  • \(\operatorname{pop}\) 的就为 \(a_j\)。
    可以知道现在一定满足 \(k = 1, t = 1\)。
    那么这个时候考虑钦定下一个 \(j'\)。
    此时相当于有 \(j - j' - 1\) 个元素还不知道填在哪里,根据上面的转移,要把这些数放到“占位符”上,也就统计了“占位符”上具体是什么值的贡献。
    占位符的数量很好算,即为 \(y - (m - j)\)(有 \(y\) 次 \(\operatorname{pop}\),且前 \(m - j\) 个已经选好了)。
    那么当 \(j - j' - 1\le cnt\) 时,有转移 \(f_{i - 1, j, 1, 1}\times \binom{cnt}{j - j' - 1}\times (j - j' - 1)!\to f_{i, j', 0, 0}\)。
  • \(\operatorname{pop}\) 的不为 \(a_j\)。
    还是一样的,不去在意是哪个元素。
    当 \(k\ge 1\) 时(\(k\not = 1\lor p\not = 1\)),有转移 \(f_{i - 1, j, k, p}\to f_{i, j, k - 1, p}\)。

时间复杂度 \(\mathcal{O}((n + m)m^2)\)。

#include<bits/stdc++.h>
using ll = long long;
constexpr ll mod = 998244353;
inline void add(ll &x, ll y) {(x += y) %= mod;}
inline ll qpow(ll a, ll b = mod - 2, ll v = 1) {
   while (b) b & 1 && ((v *= a) %= mod), b >>= 1, (a *= a) %= mod;
   return v;
}
const int maxn = 3e2 + 5;
ll f[maxn + maxn][maxn][maxn][2];
ll fac[maxn], ifac[maxn];
inline ll A(int n, int m) {return fac[n] * ifac[n - m] % mod;}
char S[maxn + maxn];
int a[maxn];
int main() {
   int n, m;
   scanf("%d%d%s", &n, &m, S + 1);
   for (int i = 1; i <= m; i++)
      scanf("%d", &a[i]);
   for (int i = fac[0] = 1; i <= m; i++)
      fac[i] = fac[i - 1] * i % mod;
   ifac[m] = qpow(fac[m]);
   for (int i = m; i; i--)
      ifac[i - 1] = ifac[i] * i % mod;
   f[0][m][0][0] = 1;
   for (int i = 1, x = 0, y = 0; i <= n + m; i++) {
      if (S[i] == '+') {
         for (int j = 0; j <= m; j++)
            for (int k = 0; k <= std::min(i, j); k++) {
               for (int t : {0, 1}) {
                  int in = x - y - k;
                  int cnt = (n - a[j]) - (m - j) - in;
                  if (in >= 0 && cnt > 0)
                     add(f[i][j][k][t], f[i - 1][j][k][t] * cnt);
                  if (k)
                     add(f[i][j][k][t], f[i - 1][j][k - 1][t]);
               }
               if (k)
                  add(f[i][j][k][1], f[i - 1][j][k - 1][0]);
            }
         x++;
      } else {
         for (int j = 0; j <= m; j++)
            for (int k = 1; k <= std::min(i, j); k++)
               for (int t : {0, 1}) {
                  if (k == 1 && t == 1)
                     continue;
                  add(f[i][j][k - 1][t], f[i - 1][j][k][t]);
               }
         for (int j = 0; j <= m; j++) {
            int cnt = y - (m - j);
            for (int j_ = 0; j_ < j; j_++)
               if (j - j_ - 1 <= cnt)
                  add(f[i][j_][0][0], f[i - 1][j][1][1] * A(cnt, j - j_ - 1));
         }
         y++;
      }
   }
   printf("%lld\n", f[n + m][0][0][0]);
   return 0;
}

标签:Atcoder,int,Priority,元素,pop,operatorname,UTPC2023P,maxn,ll
From: https://www.cnblogs.com/rizynvu/p/18331037

相关文章

  • AtCoder Beginner Contest 362
    AtCoderBeginnerContest362前言vp的时候出了四题,被C题卡了一会,很久才出,D题是dijkstra的板子,改下条件即可,E题是个计数dp,这类题一直不怎么擅长,想起之前杭电第一场那个序列立方的题也是类似这种计数dp,需要加强练习。A-BuyaPen(atcoder.jp)思路判断两两最小。......
  • Solution - Atcoder ABC280Ex Substring Sort
    对于这种子串问题,且有多个基础串,一个比较直观的想法就是先上个广义SAM。考虑SAM与字典序如何联系上。因为跳\(\operatorname{fail}\)相当于是删除子串的一个前缀,直接这样子明显是不行的,因为跳了\(\operatorname{fail}\)字典序没有一个很直观地表示。但是反过来考虑反串,......
  • (8-6-05)优先级遍历(Priority-based Search)算法:基于tkinter的多算法路径规划程序(5)
    (7)函数breadth_first_search实现了广度优先搜索算法。它使用一个队列来存储待探索的节点,并通过迭代地从队列中取出节点来搜索路径。在搜索过程中,它会调用`add_neighbours`函数来添加节点的相邻节点,并在添加节点后继续搜索。当找到目标节点时,函数会停止搜索,并调用`paint`函数来......
  • Solution - Atcoder ARC114F Permutation Division
    令\(a\)为题目中的\(P\)。首先考虑后手的重排策略是什么。因为\(a\)是个排列,元素互不相同,那么肯定就是按照每一段的第一个数的大小,越大的放在越前面。那么当\(a_1\lek\)的时候,显然先手会把以\(1\simk\)开头来划分段。因为否则存在一个开头\(>k\)的段,后手把其放......
  • 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:先判断是否有解,令......