首页 > 其他分享 >LibreOJ 2839 「JOISC 2018 Day 3」安全门

LibreOJ 2839 「JOISC 2018 Day 3」安全门

时间:2024-05-28 20:33:44浏览次数:24  
标签:le LibreOJ int mn ge JOISC 安全门 solve sn

令 \(S\) 为题面的 \(S'\)。

首先考虑刻画出 \(\texttt{()}\) 对应的折线。

首先如果 \(S\) 本身合法,那么直接 DP 算一下就行了。

否则考虑不合法的情况,考虑反转 \((l, r]\) 合法的情况的判定。
令 \(s_i\) 为前 \(S_{1\sim i}\) 的前缀和的值。
那么有:

  • \(s_r - s_l = \frac{s_n}{2}\),考虑到这样子在反转后会变为 \(s_r - s_l = -\frac{s_n}{2}\),相对差值就是 \(-s_n\),那么就能使反转后 \(s_n = 0\) 了。
  • \(i\le l\),这部分不会改变,所以就是 \(s_i\ge 0\)。
  • \(i > r\),那么 \(s_i\) 肯定都会减去 \(s_n\),所以就是 \(s_i\ge s_n\)。
  • \(l < i\le r\),发现这个反转的过程相当于是对于 \((l, r]\) 这一段关于 \(y = s_{l - 1}\) 对称了,那么就是要求 \(2s_l - s_i\ge 0\),换一种方式就是 \(s_i\le 2s_l\)。
    同时又因为 \(s_l = s_r - \frac{s_n}{2}\),有 \(s_i\le 2s_r - s_n\),两边同减 \(s_n\) 有 \((s_i - s_n)\le 2(s_r - s_n)\),这样的好处就是对于 \(i, r\) 只需要考虑其对于 \(s_n\) 的相对位置了。

记 \(mn = \min s_i\),考虑把不合法的情况分为两种:

  1. \([mn < s_0] + [mn < s_n] = 1\)。
  2. \([mn < s_0] + [mn < s_n] = 2\)。

先考虑第 \(1\) 种。

考虑如果 \(mn < s_n\),那么可以把 \(s\) 翻转并反转,相当于是对于这个折线对称过来了,那么这个时候就有 \(mn < s_0\) 了,所以就只需要考虑 \(mn < s_0\) 的情况即可。
考虑到有 \(s_i\le 2s_l, (s_i - s_n)\le 2(s_r - s_n)\)。
所以可以知道,对于前缀 \(s_i\ge 0\) 这一段,\(l\) 肯定会选取到 \(\max s_i\) 的位置。
同时因为有 \(s_n\le mn < s_0 < s_l\),肯定能找到 \(s_r = s_l + \frac{s_n}{2}\),对于 \(r\),也是贪心的选取最靠左的 \(r\),因为这样中间的 \(\max s_i\) 就能尽量小。

考虑对 \(l, r\) 进行 DP
可以设 \(f_{i, j, k}\) 为对于 \(0\sim i\),保证 \(s_i\ge 0, s_i = j, \max s_i = k\) 的方案数。
同时设 \(g_{i, j, k}\) 为对于 \(i\sim n\),保证 \((s_i - s_n)\ge (s_n - s_n) = 0, s_i - s_n = j\),选的 \(s_r\) 为 \(s_n + k\)(\(k = n + 1\) 代表这部分没选)的方案数。
转移式子不是很想写啊,看代码即可。

对于统计贡献,可以枚举最靠前的 \(s_{i} = 0, s_{i + 1} = -1\) 的位置,同时枚举下 \(s_l, s_n\)。
那么就可以得出 \(s_r\) 了。
如果 \(s_r\ge 0\),那么显然可以在 \(l\sim i\) 的位置就能找到合法的 \(r\),贡献为 \(f_{i, 0, s_l}\times g_{i + 1, -1 - sn, n + 1}\)。
否则就需要在右边选出来了,\(f_{i, 0, s_l}\times g_{i + 1, -1 - sn, sr - sn}\)。

接下来考虑第 \(2\) 种。
那么与上面差不多的,只不过对于 \(r\) 也要求 \(s_r\sim s_n\) 都需要 \(\ge s_n\),且 \(l, r\) 之间存在 \(< s_0, s_n\) 的部分。
考虑令左边合法(前缀 \(s_i\ge 0\))的部分的最大值为 \(A\),右边合法(后缀 \(s_i\ge s_n\))的部分的最大值为 \(B\)。
首先 \(s_l = A\),考虑令 \(A + \frac{s_n}{2}\le B\),即肯定能找到 \(s_r\)。
否则考虑逆着考虑这个折线(也就是上面提到的反转并翻转 \(S\)),肯定有 \(A' + \frac{s_n'}{2}\le B'\),最后再减去 \(A + \frac{s_n}{2} = B\) 这部分的贡献,因为会算重。

中间 DP 基本一致,只需要在 \(g\) 加一维 \(0 / 1\) 表示是否存在 \(s_i < s_n\) 的情况即可。
最后统计贡献的方式也大致相同。

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

#include<bits/stdc++.h>
constexpr int mod = 1e9 + 7;
inline void add(int &x, int y) {
   x += y, x >= mod && (x -= mod);
}
const int maxn = 3e2 + 10;
int n, ans;
char s[maxn];
namespace solve1 {
   int f[maxn][maxn];
   inline void solve() {
      f[0][0] = 1;
      for (int i = 0; i < n; i++)
         for (int j = 0; j <= i; j++) {
            if (s[i + 1] != ')') add(f[i + 1][j + 1], f[i][j]);
            if (s[i + 1] != '(' && j) add(f[i + 1][j - 1], f[i][j]);
         }
      add(ans, f[n][0]);
   }
}
namespace solve2 {
   int f[maxn][maxn][maxn];
   inline void solve() {
      memset(f, 0, sizeof(f));
      f[0][0][0] = 1;
      for (int i = 0; i < n; i++)
         for (int j = 0; j <= i; j++)
            for (int k = j; k <= i; k++) {
               if (s[i + 1] != ')') 
                  add(f[i + 1][j + 1][std::max(j + 1, k)], f[i][j][k]);
               if (s[i + 1] != '(' && j) 
                  add(f[i + 1][j - 1][k], f[i][j][k]);
            }
   }
}
namespace solve3 {
   int g[maxn][maxn][maxn];
   inline void solve() {
      memset(g, 0, sizeof(g));
      g[n][0][0] = g[n][0][n + 1] = 1;
      for (int i = n; i; i--)
         for (int j = 0; j <= n - i; j++) {
            if (s[i] != '(') 
               add(g[i - 1][j + 1][n + 1], g[i][j][n + 1]), add(g[i - 1][j + 1][j + 1], g[i][j][n + 1]);
            if (s[i] != ')' && j)
               add(g[i - 1][j - 1][n + 1], g[i][j][n + 1]), add(g[i - 1][j - 1][j - 1], g[i][j][n + 1]);
            for (int k = 0; k <= n - i; k++) {
               if (s[i] != '(' && j + 1 <= k * 2 && j + 1 != k)
                  add(g[i - 1][j + 1][k], g[i][j][k]);
               if (s[i] != ')' && j && j - 1 <= k * 2 && j - 1 != k)
                  add(g[i - 1][j - 1][k], g[i][j][k]);
            }
         }
      for (int l = 0; l < n; l++) {
         if (s[l + 1] == '(')
            continue;
         for (int sl = 0; sl <= l; sl++)
            for (int sn = -n; sn; sn += 2) {
               int sr = sl + sn / 2;
               add(ans, 1ll * solve2::f[l][0][sl] * g[l + 1][-1 - sn][sr >= 0 ? (n + 1) : (sr - sn)] % mod);
            }
      }
   }
}
int g[maxn][maxn * 2][maxn][2];
namespace solve4 {
   inline void solve() {
      memset(g, 0, sizeof(g));
      g[n][n][0][0] = g[n][n][n + 1][0] = 1;
      for (int i = n; i; i--)
         for (int j = n - (n - i); j <= n + (n - i); j++) {
            if (s[i] != '(') {
               add(g[i - 1][j + 1][n + 1][0], g[i][j][n + 1][0]);
               j + 1 >= n && (add(g[i - 1][j + 1][j + 1 - n][0], g[i][j][n + 1][0]), 1);
            }
            if (s[i] != ')' && j - 1 >= n) {
               add(g[i - 1][j - 1][n + 1][0], g[i][j][n + 1][0]);
               add(g[i - 1][j - 1][j - 1 - n][0], g[i][j][n + 1][0]);
            }
            for (int k = 0; k <= n - i; k++) {
               if (s[i] != '(' && j + 1 - n <= k * 2) {
                  j + 1 - n != k && (add(g[i - 1][j + 1][k][0], g[i][j][k][0]), 1);
                  add(g[i - 1][j + 1][k][1], g[i][j][k][1]);
               }
               if (s[i] != ')' && j - 1 - n <= k * 2) {
                  j - 1 - n != k && (add(g[i - 1][j - 1][k][j - 1 < n], g[i][j][k][0]), 1);
                  add(g[i - 1][j - 1][k][1], g[i][j][k][1]);
               }
            }
         }
      for (int l = 0; l < n; l++) {
         if (s[l + 1] == '(')
            continue;
         for (int sl = 0; sl <= l; sl++)
            for (int sn = -n; sn < n; sn += 2) {
               int sr = sl + sn / 2;
               sr - sn >= 0 && (add(ans, 1ll * solve2::f[l][0][sl] * g[l + 1][-1 - sn + n][sr - sn][1] % mod), 1);
            }
      }
   }
}
namespace solve5 {
   inline void solve() {
      memset(g, 0, sizeof(g));
      g[n][n][0][0] = 1;
      for (int i = n; i; i--)
         for (int j = n - (n - i); j <= n + (n - i); j++) {
            for (int k = 0; k <= n - i; k++) {
               if (s[i] != '(') {
                  add(g[i - 1][j + 1][std::max(k, j + 1 - n)][0], g[i][j][k][0]);
                  j + 1 - n <= k * 2 && (add(g[i - 1][j + 1][k][1], g[i][j][k][1]), 1);
               }
               if (s[i] != ')') {
                  add(g[i - 1][j - 1][k][j - 1 < n], g[i][j][k][0]);
                  j - 1 - n <= k * 2 && (add(g[i - 1][j - 1][k][1], g[i][j][k][1]), 1);
               }
            }
         }
      for (int l = 0; l < n; l++) {
         if (s[l + 1] == '(')
            continue;
         for (int sl = 0; sl <= l; sl++)
            for (int sn = -n; sn < n; sn += 2) {
               int sr = sl + sn / 2;
               sr - sn >= 0 && (add(ans, mod - 1ll * solve2::f[l][0][sl] * g[l + 1][-1 - sn + n][sr - sn][1] % mod), 1);
            }
      }
   }
}
int main() {
   scanf("%d%s", &n, s + 1);
   if (n & 1)
      return puts("0"), 0;
   solve1::solve();
   solve2::solve();
   solve3::solve();
   solve4::solve();
   solve5::solve();
   std::reverse(s + 1, s + n + 1);
   for (int i = 1; i <= n; i++)
      s[i] != 'x' && (s[i] ^= '(' ^ ')');
   solve2::solve();
   solve3::solve();
   solve4::solve();
   printf("%d\n", ans);
   return 0;
}

标签:le,LibreOJ,int,mn,ge,JOISC,安全门,solve,sn
From: https://www.cnblogs.com/rizynvu/p/18218794

相关文章

  • LOJ#4149. 「JOISC 2024 Day1」滑雪 2
    首先,不存在\(H_i<H_j\)时增高\(H_i\)至\(H_j+1\)后连\(i\toj\)更优,因为增高后原来能连\(i\)的点现在不一定能连\(i\)了,原来能连\(j\)的点还是能连\(j\),此时的方案集必然是原方案集的子集,答案一定不会更优,又因为你付出了增高的费用,所以这样一定劣。那么我们......
  • LibreOJ 3818 「CEOI2022」Parking
    考虑把这个停车位当作栈来考虑,每次可以取出栈顶放到另一个栈,并且要保证另一个栈\(sz\le2\),且当\(sz=2\)时要保证栈顶栈底都是同一种元素。令\((x,y)\)表示\(x\)为栈顶\(y\)为栈底,\((0,x)\)表示栈中元素只有\(x\)。考虑对于\((x,y)\),连一条\(x\toy\)的边,若......
  • JOISC 2024 记录
    感觉我太滞后了Day1T1Fish3我们可以做的操作是单点加\(D\)和后缀加\(1\),考虑把这个操作放在差分数组上,则操作变成了:单点加\(1\)。\(i\)处加\(D\),\(i+1\)处\(-D\)。需要最小化第二种操作的使用次数,发现只有对于所有差分数组中的负数是不得不用第二种操作的,而对于......
  • loj#575. 「LibreOJ NOI Round #2」不等关系
    记事件\(A\)为「当\(s_i=\texttt<\)时\(p_i<p_{i+1}\)」,事件\(B\)为「当\(s_i=\texttt<\)时\(p_i<p_{i+1}\),且存在\(s_j=\texttt>\),满足\(p_i<p_{i+1}\)。所求即\(n(A)-n(B)\)。\(n(A)\)是好求的,相当于部分定序排列,记每个递增段的长度为\(a_1......
  • loj#566. 「LibreOJ Round #10」yanQval 的生成树
    \(\mu\)取值即所选边权的中位数。把每条边拆成两条(黑边和白边),边权分别为\(\mu-w_i\)和\(w_i-\mu\),要求黑边和白边各选\(\left\lfloor\dfrac{n-1}2\right\rfloor\)条,求最大生成树。可以直接wqs二分,时间复杂度\(\mathcalO(nm\logw~\alpha(n))\)。把所有边的边权......
  • loj#546. 「LibreOJ β Round #7」网格图
    裸的01BFS,时间复杂度\(\mathcalO(nm)\)。相邻的无障碍行可以缩成一行,列同理,所以全图的规模可以缩成\((k+1)\times(k+1)\),再01BFS,时间复杂度\(\mathcalO(k^2)\)。进一步地,所有\(1\timest\)或\(t\times1\)大小的无障碍连通块均可缩成一个点,两个连通块相交,则......
  • loj#523. 「LibreOJ β Round #3」绯色 IOI(悬念)
    由题述,\(X\)满匹配,根据Hall定理,有对于任意一个有\(k\)个妹子的集合,她们能配对的男生的人数\(\gek\)。把每个妹子看作她所连接的两个(可能是同一个)男生间的无向边,则每个连通块必然是树或基环树。问题转化为给每条无向边定向,满足每个点的入度不超过\(1\),求最大边权和。对......
  • 历史研究(洛谷AT_joisc2014_c 歴史の研究)
    历史研究(洛谷AT_joisc2014_c 歴史の研究)题目描述IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。日记中记录了连续N天发生的事件,大约每天发生一件。......
  • P9527 [JOISC2022] 洒水器 题解
    题目传送门以下设\(\operatorname{dis}(x,y)\)表示树上\(x,y\)两点间的距离。修改时对\(u\)的周围与\(u\)距离小于等于\(d\)的点的点权乘\(w\)。暴力不行,于是考虑打标记。注意到\(0\led\le40\),一个很自然的想法是:设\(tag(x,i)\)表示将\(x\)的子树内与\(x\)......
  • [分块] [Luogu AT_joisc2014_c] 历史研究
    题目描述IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。日记中记录了连续\(N\)天发生的事件,大约每天发生一件。事件有种类之分。第\(i\)天发生的......