首页 > 其他分享 >AGC019 D~F【杂题】

AGC019 D~F【杂题】

时间:2022-11-01 21:37:09浏览次数:62  
标签:int 类点 AGC019 ret ++ 杂题 inv mod

D. Shift and Flip

给定两个 \(01\) 串 \(A\) 和 \(B\),每次操作可以将 \(A\) 循环左移或右移一位,或选择一个 \(B_i = 1\) 的位置将 \(A_i\) 取反,求使 \(A\) 和 \(B\) 相等至少要进行几次操作。

\(n \leq 2 \times 10^3\)。


显然,无解当且仅当 \(B\) 中不存在 \(1\),且 \(A\) 中存在 \(1\)。

结合数据范围,容易想到先枚举最终 \(A\) 和 \(B\) 的对齐方式,这时操作 \(3\) 的次数是固定的,也就是 \(A\) 和 \(B\) 不同的位置个数。现在我们要最小化前两个操作的次数和。

对每个 \(i\),我们求出 \(p_i\) 表示使得 \(b_{i - j} = 1\) 的 \(j\) 的值,\(q_i\) 表示使得 \(b_{i+j} = 1\) 的 \(j\) 的值(下标运算默认在 \(\text{mod} \ n\) 意义下进行)。利用调整法容易说明如下事实:设 \(L\) 为左移长度,\(R\) 为过原点后右移长度,那么最优方案中 \(A\) 的移动一定是分为两段:向左移动 \(L\),回到原点,向右移 \(R\)(或者相反),然后和 \(B\) 对齐。并且,\(L\) 一定恰等于某个 \(p_i\),\(R\) 一定恰等于某个 \(q_i\)。

考虑每次枚举对齐方式后如何求出 \(L,R\):我们将所有没有匹配上的位置找出来,那么 \(L,R\) 合法当且仅当对于每一个失配点 \(i\) 都有 \(L \geq p_i\) 或 \(R \geq q_i\)。将二元组 \((p_i,q_i)\) 以 \(q_i\) 为关键字从小到大排序,设 \(f_i\) 为 \(p_i\) 的后缀最大值,那么所有可能最优的合法解即为 \(L_i =f_{i+1},R_i = q_i\),枚举即可。

由于 \(\forall i,q_i < n\),使用基数排序,时间复杂度 \(O(n^2)\)。

code
/*
挥拂去蒙尘半生的晦暗
用这嶙峋双臂迎接 振翅吧 我的蝴蝶
最后一支 赤诚赞歌盘旋
潮起潮落翻覆昼夜 澎湃在生命刻度之前
此刻色彩挣脱谎言 献予你 赤红纸花遍野
*/
#include <bits/stdc++.h>
#define mem(x, v) memset(x, v, sizeof(x))
using namespace std;
const int N = 2e3 + 5, inf = 0x3f3f3f3f;

int n, m, ans, L[N], R[N], buk[N], vr[N], suf[N], tmp[N];
char s[N]; bool a[N], b[N], sa, sb;

int main() {
    scanf("%s", s), n = strlen(s);
    for (int i = 0; i < n; i++) a[i] = s[i] - '0';
    scanf("%s", s);
    for (int i = 0; i < n; i++) b[i] = s[i] - '0';
    mem(L, inf), mem(R, inf);
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) if (b[j])
            L[i] = min(L[i], (i - j + n) % n), R[i] = min(R[i], (j - i + n) % n);
    for (int i = 0; i < n; i++) sa |= a[i], sb |= b[i];
    if (!sb) return puts(sa ? "-1" : "0"), 0;
    ans = inf;
    for (int i = 0; i < n; i++) {
        m = 0;
        for (int j = 0, k = i; j < n; j++, k++, k %= n) if (a[k] != b[j]) vr[++m] = k;
        for (int i = 0; i <= n; i++) buk[i] = 0;
        for (int i = 1; i <= m; i++) buk[R[vr[i]]]++;
        for (int i = 1; i <= n; i++) buk[i] += buk[i - 1];
        for (int i = m; i >= 1; i--) tmp[buk[R[vr[i]]]--] = vr[i];
        for (int i = 1; i <= m; i++) vr[i] = tmp[i];
        suf[m + 1] = 0;
        for (int j = m; j >= 1; j--) suf[j] = max(suf[j + 1], L[vr[j]]);
        ans = min(ans, max(i, suf[1]) * 2 - i + m);
        ans = min(ans, (suf[1] + (n - i) % n) * 2 - (n - i) % n + m);
        for (int j = 1; j <= m; j++) {
            ans = min(ans, (R[vr[j]] + max(i, suf[j + 1])) * 2 - i + m);
            ans = min(ans, (suf[j + 1] + max((n - i) % n, R[vr[j]])) * 2 - (n - i) % n + m);
        }
    }   
    cout << ans << endl;
    return 0;
}

E. Shuffle and Swap

给定两个长度为 \(n\) 的 \(01\) 串 \(A\) 和 \(B\),两个串均有 \(k\) 个 \(1\)。

令 \(a_{1\sim k}\) 和 \(b_{1\sim k}\) 分别表示 \(A\) 和 \(B\) 中所有 \(1\) 出现的位置。现将 \(a\) 和 \(b\) 等概率随机排列,按 \(1\sim k\) 的顺序交换 \(A_{a_i}\) 和 \(A_{b_i}\),求有多少种不同的排列方式使得操作结束后 \(A\) 和 \(B\) 相等。答案对 \(998244353\) 取模。

\(n \leq 10^4\),时限 \(\text{2.0s}\)。


不妨先分析一下操作的结构:

我们将所有点分成三类:\(A_i = 0\) 且 \(B_i = 1\),\(A_i = 1\) 且 \(B_i = 0\),以及 \(A_i = B_i = 1\)。

我们的目标是,将所有 \(1\) 类点的 \(0\) 传出去,其中可能经过一些 \(3\) 类点,然后到达一个 \(2\) 类点。换句话说,我们需要将所有 \(1\) 类点和 \(2\) 类点匹配。

为了更精确地刻画这个过程,我们建立这样一张图:如果操作了 \(a_i,b_i\) 就连边 \(b_i \to a_i\),那么此时 \(1\) 类点出度为 \(1\),\(2\) 类点入度为 \(1\),\(3\) 类点入度和出度均为 \(1\)。也就是说,这张图一定由若干链和环组成,其中链的条数是确定的,也就是 \(1\) 类点的数量。

不妨考虑有多少 \(3\) 类点在链上,我们枚举这个值,设 \(1\) 类点有 \(p\) 个,\(3\) 类点有 \(q\) 个,\(f_{i,j}\) 为有 \(i\) 个 \(3\) 类点,组成 \(j\) 条链的排列数,那么答案即为

\[\sum_{k=0}^q \binom{q}{k} \binom{p+q}{k} \cdot (k!)^2 \cdot f_{p,q-k} \]

其组合意义显然,\((k!)^2\) 是因为剩下的 \(3\) 类点可以随便匹配。现在的问题变成了如何求 \(f\)。考虑转移:

  • 在序列末尾新建一条链,这需要选择一个 \(1\) 类点和一个 \(2\) 类点,因此 \(f_{i,j} \gets f_{i-1,j} \cdot i^2\)。

  • 在某条链的末尾插入一个 \(3\) 类点,这需要选择一条链以及一个 \(3\) 类点,因此 \(f_{i,j} \gets f_{i,j - 1} \cdot i \cdot j\)。

综上可得 \(f_{i,j} = f_{i-1,j} \cdot i^2 + f_{i,j-1} \cdot i \cdot j\),由于我们每次只在末尾插入,因此不会算重。直接算就行了,时间复杂度 \(O(n^2)\)。

code
/*
挥拂去蒙尘半生的晦暗
用这嶙峋双臂迎接 振翅吧 我的蝴蝶
最后一支 赤诚赞歌盘旋
潮起潮落翻覆昼夜 澎湃在生命刻度之前
此刻色彩挣脱谎言 献予你 赤红纸花遍野
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5, mod = 998244353;

int qpow(int a, int b = mod - 2, int ret = 1) {
    while (b) {
        if (b & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod, b >>= 1;
    }
    return ret;
}

int n, p, q, ans, f[N][N], fac[N], inv[N]; 
char a[N], b[N];

int main() {    
    scanf("%s %s", a + 1, b + 1), n = strlen(a + 1);
    for (int i = 1; i <= n; i++) p += (a[i] == '1' && b[i] == '0'), q += (a[i] == '1' && b[i] == '1');
    fac[0] = 1;
    for (int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
    inv[n] = qpow(fac[n]);
    for (int i = n; i >= 1; i--) inv[i - 1] = 1ll * inv[i] * i % mod; 
    for (int i = 0; i <= p; i++) f[i][0] = 1ll * fac[i] * fac[i] % mod;
    for (int i = 1; i <= p; i++)
        for (int j = 1; j <= q; j++)
            f[i][j] = 1ll * (1ll * f[i - 1][j] * i % mod + 1ll * f[i][j - 1] * j % mod) % mod * i % mod;
    for (int i = 0; i <= q; i++)
        ans = (ans + 1ll * fac[q] * fac[p + q] % mod * ifac[q - i] % mod * ifac[p + q - i] % mod * f[p][q - i] % mod) % mod;
    cout << ans << endl;
    return 0;
}

F. Yes or No

有 \(n+m\) 个问题,其中有 \(n\) 个问题的答案是 Yes,\(m\) 个问题的答案是 No。当你回答一个问题之后,会知道这个问题的答案,求最优策略下期望对多少。答案对 \(998244353\) 取模。

\(n,m \leq 10^5\)。


感性理解一下,我们的最优策略是:假设此时已知 Yes 有 \(n\) 个而 No 有 \(m\) 个,若 \(n > m\) 则回答 Yes,若 \(n < m\) 回答 No,否则回答 Yes 或 No 均可。

把这个问题放到数轴上考虑,点 \((n,m)\) 表示当前 Yes 有 \(n\) 个而 No 有 \(m\) 个,那么一道题答案为 Yes 相当于往左走一步,答案为 No 相当于往下走一步。那么我们回答的过程相当于是一条 \((n,m) \to (0,0)\) 的路径。

我们画一条直线 \(y=x\),如果不考虑直线上的点,那么答案显然是 \(\max(n,m)\)。考虑这条直线对答案的影响:位于 \(y=x\) 上的点表示剩下的问题中,答案为 Yes 和 No 的一样多,这时我们猜对的概率为 \(\frac{1}{2}\)。于是只需要计算每个 \(y=x\) 上的点被经过多少次即可算出直线的额外贡献。

经过点 \((i,i)\) 的方案数为 \(\binom{2i}{i} \times \binom{n+m -2\times i}{n-i}\),而总路径数为 \(\binom{n+m}{m}\),所以一个点 \((i,i)\) 的贡献就是 \(\dfrac{1}{2} \times \dfrac{\binom{2i}{i} \times \binom{n+m -2\times i}{n-i}}{\binom{n+m}{m}}\)。据此容易计算总贡献,时间复杂度 \(O(n)\)。

code
/*
挥拂去蒙尘半生的晦暗
用这嶙峋双臂迎接 振翅吧 我的蝴蝶
最后一支 赤诚赞歌盘旋
潮起潮落翻覆昼夜 澎湃在生命刻度之前
此刻色彩挣脱谎言 献予你 赤红纸花遍野
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5, mod = 998244353;

int qpow(int a, int b = mod - 2, int ret = 1) {
    while (b) {
        if (b & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod, b >>= 1;
    }
    return ret;
}

int n, m, fac[N], inv[N];
void add(int &x, int y) { x = (x + y >= mod) ? (x + y - mod) : (x + y); }
int C(int n, int m) { 
    return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod; 
}

int main() {    
    ios :: sync_with_stdio(0);
    cin >> n >> m; int L = 2 * max(n, m);
    fac[0] = inv[0] = 1;
    for (int i = 1; i <= L; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
    inv[L] = qpow(fac[L]);
    for (int i = L; i >= 1; i--) inv[i - 1] = 1ll * inv[i] * i % mod;
    int ans = 0;
    for (int i = 1; i <= min(n, m); i++) add(ans, 1ll * C(2 * i, i) * C(n - i + m - i, n - i) % mod);
    ans = 1ll * ans * inv[2] % mod * inv[n + m] % mod * fac[n] % mod * fac[m] % mod;
    add(ans, max(n, m));
    cout << ans << endl;
    return 0;
}

标签:int,类点,AGC019,ret,++,杂题,inv,mod
From: https://www.cnblogs.com/came11ia/p/16845910.html

相关文章

  • AGC018 C~F【杂题】
    C.Coins有\(x+y+z\)个人,第\(i\)个人有\(a_i\)个金币,\(b_i\)个银币,\(c_i\)个铜币。选\(x\)个人获得金币、\(y\)个人获得银币、\(z\)个人获得铜币,不重复选人,最......
  • 2022.10 杂题
    P8569[JRKSJR6]第七学区首先,有若干种\(O(n\logV)\)的暴力。我们选择其中操作比较简单的一种:对于每一位,先求出所有\(0\)段的长度之和,然后用所有区间的长度之和减掉......
  • Solution Set - 杂题题解(二)
    目录「CF1726E」AlmostPerfect「CF95E」LuckyCountry「CF1245F」DanielandSpringCleaning「CF1372E」OmkarandLastFloor「AGC038C」LCMs/「LG-P3911」最小公倍数......
  • CSP-S模拟14 ~ CSP-S模拟17 杂题选讲
    \(\text{Preface}\)我觉得殷教说的很对。如果说强行让我写之前咕掉的总结我觉得意义不大,而且我大多数题也都忘了再写思路不一定对而且有亿点敷衍,所以我就写其中一部分吧......
  • 字符串杂杂杂杂杂杂题
    [CF1310C]AuPontRouge首先,肯定要将所有的代价给弄出来,若先不管划分段数的限制,那么所有代价就是\(S\)的所有字串那么字串的数量也就是\(\frac{n*(n+1)}{2}\),约为\(10^6......
  • 10月杂题
    ??怎么已经十月了???1.MagicMatrix首先意识到\(a_{i,j}=a_{j,i}\)是关键性质。Solution1:令\(d_{i,j}=\min\{\max\{a_{i,k},a_{k,j}\}\mid1\lek\len\}\),考虑求所有......
  • 贪心杂题
    \(Preface\)最近咋老考贪心嘞?我的数据结构呢?贪心这玩意确实重要。但是我蒟蒻,老做不出来,而且经常写挂某些需要用到一些小技巧的题。。。具体地,刷贪心题既是刷思维也是刷......
  • ARC杂题
    实际上如果你觉得你切了两道题但是没拍的话那就真的会保龄。所以我挂了200。警钟长鸣。ARC125C使字典序最小的话,他给了\(k\)个我们需要扔掉最后一个不看,要不然可能不优......
  • 9 月杂题选做
    觉得比较有意思的题,坑LuoguP6824「EZEC-4」可乐\(k\)是已知的,我们要确定一个\(x\),不妨直接枚举这个\(x\)。显然\(x\leq2^{21}\),然后涉及异或,考虑字典树。......
  • 【杂题合集】震惊!这种生活习惯可能致癌!99%的人都有......
    杂题合集标题党去死CF1714EAddModulo10题意概述:给出一个序列,可以给其中任意一个数加上当前它的个位数,问进行若干次操作后这个序列里的数能否全部相等。解析:思维......