D. Shuffle(cf)暴力枚举 + 组合数学

原题链接:Problem - 1622D - Codeforces

题目大意:给你一串01串,告诉你长度n和一个数k,这个串中所有1的数量为k的子串,可以把这段子串重新排序。问你最后这个串能有多少种。

思路:看第一个案例就知道会有重复的情况,但是区间重复不好算,又因为n为5000, n * n能跑,所以暴力枚举i, j(j > i,因为区间为一个长度的时候它跟谁换呢,换不了)作为第一个改变的地方和最后一个改变的地方。如果i ~ j区间1的数量<= k,就可以进行这样的操作。因为i和j下标的地方一定会改变,设i到j区间中1d个数是cnt,那么如果左右端点是0,就要用到1,所以左右端点i j有几个0就把cnt减几。然后除了左右端点,中间有i - j - 1个数,要把更新了的cnt个1分配给i - j - 1个地方,所以就是C(j - i - 1, cnt)。还有一点要注意的地方,如果一个地方都不修改,那么就是原串,所以一开始ans为1。

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int, int> PII;
const double pi = acos(-1.0);
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define rrep(i, n) for (int i = n; i >= (1); --i)
typedef long long ll;
#define sqar(x) ((x)*(x))

const ll M = 998244353;
const int N = 5010;
char s[N];
int a[N], pre[N];
ll fac[N], invfac[N];

inline ll qpow(ll a, ll n, ll p)// 快速幂
{
    ll ans = 1;
    while (n)
    {
        if (n & 1)
            ans = ans % p * a % p;
        a = a % p * a % p;
        n >>= 1;
    }
    return ans;
}

inline ll inv(ll a, ll p)
{
    return qpow(a, p - 2, p);
}

void init() //注意要在主函数中init()!!!
{
     fac[0] = 1;
     for (int i = 1; i < N; ++i) fac[i] = (fac[i - 1] * i) % M;
     invfac[N - 1] = inv(fac[N - 1], M);
     for (int i = N - 2; i >= 0; --i) invfac[i] = (invfac[i + 1] * (i + 1)) % M;
}

ll C(int n, int m)
{
    if (n < m || m < 0) return 0;
    return fac[n] * invfac[m] % M * invfac[n - m] % M;
}

int main()
{
    init();
    int n, k;
    scanf("%d %d", &n, &k);
    scanf("%s", s + 1);
    rep(i, n) a[i] = s[i] - '0';
    rep(i, n) pre[i] = pre[i - 1] + a[i];
    ll ans = 1;
    if(pre[n] < k){puts("1"); return 0;}
    rep(i, n)
     for(int j = i + 1; j <= n; j++){
        if(pre[j] - pre[i - 1] > k) break;
        int cnt = pre[j] - pre[i - 1];
        cnt -= (a[i] == 0) + (a[j] == 0);
        ans = (ans + C(j - i - 1, cnt)) % M;
     }
    printf("%lld", ans);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值