首页 > 其他分享 >Shuffle 题解

Shuffle 题解

时间:2023-10-11 17:56:15浏览次数:39  
标签:Shuffle int 题解 inv choose return include mod

Shuffle

题目大意

给定一个长度为 \(n\) 的 01 序列 \(a\),你可以进行至多一次以下操作:

  • 选定 \(a\) 的一个连续段,满足连续段内恰好有 \(k\) 个 \(1\),将该连续段任意排列。

问能产生多少种不同的 01 序列。

思路分析

(这题 \(n\) 完全可以开到 \(10^6\) 或是 \(10^7\),因为存在 \(O(n)\) 的做法。)

考虑 DP。

设 \(f_i\) 表示只考虑 \(1\sim i\) 中的字符,能产生多少种不同的 01 序列。

那么可以列出 DP 方程:

\[f_i=\begin{cases}f_{i-1}+{m-1\choose k-1}&s_i=0\\f_{i-1}+{m-1\choose k}&s_i=1\end{cases} \]

其中,\(m\) 是 \(i\) 往左的极长 \(k\) 个 \(1\) 的连续段的长度。

解释一下:

我们在考虑 \(f_{i-1}\) 时,是把 \(s_i\) 恒定为 \(s_i\) 做的,而在考虑 \(f_i\) 时为了避免算重,我们强制钦定在 \(i\) 的位置放 \(s_i\) 的相反的数,也就是说,若 \(s_i=0\),我们强制这个位置放 \(1\),那么方案数就是 \({m-1\choose k-1}\),在前 \(m-1\) 个位置上放剩下的 \(k-1\) 个数。\(s_i=1\) 类似。

注意初值,当 \(m\) 第一次存在时,\(f_i={m\choose k}\),这是因为 \(f_i\) 前面没有人算它的重。

实现方式有很多种,我使用了二分实现,时间复杂度为 \(O(n\log n)\)。

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>

using namespace std;
const int N = 1001000, L = 1000000, mod = 998244353;
#define inf 0x3f3f3f3f
#define int long long

int n, k, ans;
int fac[N], inv[N], sum[N];

set <int> S;

char s[N];

int q_pow(int a, int b){
    int res = 1;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

int C(int n, int m){
    if (n < m) return 0; 
    return fac[n] * inv[n - m] % mod * inv[m] % mod;
}

int find(int s, int k){ // 找到从 s 往左的第 k + 1 个 1 的位置的右边
    if (sum[s] < k) return inf;
    int l = 1, r = s, ans = 1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (sum[s] - sum[mid - 1] <= k) r = mid - 1, ans = mid;
        else l = mid + 1;
    }
    return *(--S.lower_bound(ans)) + 1;
}

signed main(){
    fac[0] = 1;
    for (int i = 1; i <= L; i ++) fac[i] = fac[i - 1] * i % mod;
    inv[L] = q_pow(fac[L], mod - 2);
    for (int i = L; i >= 1; i --) inv[i - 1] = inv[i] * i % mod;
    scanf("%lld %lld", &n, &k);
    scanf("%s", s + 1);
    for (int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + (s[i] == '1');
    S.insert(0);
    for (int i = 1; i <= n; i ++) if (s[i] == '1') S.insert(i);
    int flag = 0;
    for (int i = 1; i <= n; i ++) {
        if (sum[i] == k && !flag) ans = (ans + C(i, k)) % mod, flag = 1;
        else ans = (ans + C(i - find(i, k), k - (s[i] == '0'))) % mod;
    }
    if (!flag) ans = 1;
    cout << ans << '\n';
    return 0;
}

标签:Shuffle,int,题解,inv,choose,return,include,mod
From: https://www.cnblogs.com/TKXZ133/p/17757819.html

相关文章

  • Hadoop问题解决(5)
    当一个HDFS系统同时处理许多个并行的put操作,往HDFS上传数据时,有时候会出现dfsclient端发生socket链接超时的报错,有的时候甚至会由于这种原因导致最终的put操作失败,造成数据上传不完整。log类似如下:Alldatanodes ***arebad.Aborting...类似这样的错误,常常会在并行的put操作......
  • Debian12安装MySQL8实践及问题解决方案
    Debian12安装MySQL数据库,常规操作:sudoaptsearchmysql&sudoaptinstallmysql,肯定是行不通的,因为没有安装包。把我的安装过程以及遇到问题的解决方案记录下来,供大家借鉴。第一步更新系统、下载软件包命令如下:sudoaptupdatewgethttps://dev.mysql.com/get/mysql-apt-co......
  • 【题解 P3586】 LOG
    [POI2015]LOG题目描述维护一个长度为\(n\)的序列,一开始都是\(0\),支持以下两种操作:Uka将序列中第\(k\)个数修改为\(a\)。Zcs在这个序列上,每次选出\(c\)个正数,并将它们都减去\(1\),询问能否进行\(s\)次操作。每次询问独立,即每次询问不会对序列进行修改。输......
  • 题解 AcWing 359.创世纪
    题目描述给你一个\(n\)个点\(n\)条边的有向图,若选了当前节点,那么当前节点的儿子节点至少有一个不能选。求最多能选多少个点。具体思路显然是一棵基环树,因此考虑基环树dp。我们先不管环的条件,先考虑朴素的树形dp。设\(f_{x,0}\)表示\(x\)节点不选,最多可以选多少个......
  • 网络规划设计师真题解析--位示图大小计算
    假设某计算机的字长为32位,该计算机文件管理系统磁盘空间管理采用位示图,记录磁盘的使用情况,若磁盘的容量为300GB,物理块的大小为4MB,那么位示图的大小为(2)个字。(2020年)(2)A.2400 B.3200 C.6400 D.9600答案:A解析:已知磁盘容量为300GB,物理块大小为4MB则计算物理块数=300*1024/4=76800(个)位......
  • 题解: CF768D Jon and Orbs
    题解:CF768DJonandOrbs一句话体面:有k种不同的物品,每天等概率任取一种(不一定是新的种类)。q组询问,每组给出一个p,问取完这k件物品的概率不小于\(\frac{p}{2000}\)的最小天数不用说,肯定是概率DP了1.定义:\(f_{i,j}\)表示前\(i\)天选取了\(j\)种物品的概率(\(P.S.\)该定义不......
  • 题解 DIFERENC - DIFERENCIJA
    题目描述给出一个长度为\(n\)的序列\(a_i\),求出下列式子的值:\[\sum_{i=1}^n\sum_{j=i}^n(\max\limits_{i\lek\lej}a_k-\min\limits_{i\lek\lej}a_k)\]即定义一个子序列的权值为序列内最大值与最小值的差。求出所有连续子序列的权值和。具体思路暴力思路很好......
  • p4801题解
    解题思路:确实是一道很好的贪心,但由于加上了水这个影响因素,使题目复杂度上升了不少。(考虑的东西多了嘛)输个入。对饼干温度无脑排序。求最小值。求最大值(用双指针做,后面会讲)。解题过程:先输入(这个步骤就不用我讲了)inta[1000005];longlongn,ws;longlongmin......
  • 洛谷P4158 [SCOI2009] 粉刷匠 题解
    所有的\(DP\),只要式子一推出来(不管复杂度),那就很简单了,因为优化是成千上万种的……思路1:我们考虑设\(f[i][j][k]\)表示:当前\(DP\)到第\(i\)块木板的第\(j\)个位置,共涂了\(k\)次,所能获得的最大收益。因为还要枚举当前这次涂是从哪到哪的,因此复杂度为\(O(NM^2T)\),实......
  • 洛谷P3300 [SDOI2013] 城市规划 题解
    [SDOI2013]城市规划题意:给你一个\(6\timesn\)的网格题,单点修改,询问区间联通块数,\(n\le10^5\)。解:看起来就很显然的一道题......线段树每个点用一个ufs维护连通性;我为了方便思考把图转成横着的了。写起来真是毒瘤......重点在于:\(\bullet\)1、建立叶节点。\(\bull......