Educational Codeforces Round 120 (Rated for Div. 2) D

D:(组合数学,双指针,容斥)(双指针实际上用来实现容斥)

你可以对字符串s最多进行一次如下操作:选择s中正好有k个字符1的子串(一个连续的子序列),并对其进行洗牌(按你的意愿重新排列子串中的字符)。

计算通过最多一次的操作可以从s中得到多少个不同的字符串。

输入
第一行包含两个整数n和k(2≤n≤5000;0≤k≤n)。

第二行包含长度为n的字符串s,由字符0和/或1组成。

输出
打印一个整数--通过最多执行一次所述操作可以从s中得到的不同字符串的数量。因为答案可能很大,所以输出它的模数998244353。

 

思路:

假设找到cnt段最优子串,对于第i串 l[i]-r[i]:

 

 

#include<bits/stdc++.h>
using namespace std;
using i64=long long;
typedef long long ll;
typedef double db;
typedef long double ldb;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<int,ll> pil;
typedef pair<ll,int> pli;
typedef pair<ll,ll> pll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<ull> vull;
#define IOS ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
#define fi first
#define se second 
#define pb push_back
#define eb emplace_back
#define mkp make_pair
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b)    for(int i=a;i<b;i++)
#define per(i,a,b)    for(int i=a;i>b;i--)
#define repp(i,a,b)    for(ll i=a;i<b;i++)
#define perr(i,a,b)    for(ll i=a;i>b;i--)
#define sayyes    cout<<"YES"<<"\n"
#define sayno    cout<<"NO"<<"\n"
const int N=1e6+30;
const ll mod=998244353;
//组合数学
ll fac[N],pre[N],a[N],l[N],r[N];
ll qpow(ll a,ll b){
    ll ans=1%mod;
    for(;b;b>>=1){
        if(b&1)    ans=ans*a%mod;
        a=a*a%mod;
    }
    return ans;
}
ll cal(ll a,ll b){
    if(a<0||b<0||a>b)    return 0;
    else return fac[b]%mod*pre[a]%mod*pre[b-a]%mod;
}
int main(){
    IOS;
    //another math?
    ll n,k;
    cin>>n>>k;
    fac[0]=1;
    pre[0]=1;
    repp(i,1,N){
        fac[i]=fac[i-1]*i%mod;
        pre[i]=qpow(fac[i],mod-2);
    }
    string s;
    cin>>s;
    ll num=0;
    repp(i,0,n){
        if(s[i]=='1')    a[++num]=i;
    }
    a[0]=-1,a[num+1]=n;
    if(num<k||k==0)    cout<<1<<"\n";
    else{
        ll ans=0;
        ll cnt=0;
        repp(i,0,num-k+1){
            l[++cnt]=a[i]+1;
            r[cnt]=a[i+k+1]-1;
        }
        repp(i,1,cnt+1){
            ans=(ans+cal(k,r[i]-l[i]+1))%mod;
        }
        repp(i,2,cnt+1){
            ans=(ans-cal(k-1,r[i-1]-l[i]+1))%mod;
        }
        ans=(ans+mod)%mod;
        cout<<ans<<"\n";
    }
    return 0;
}


//纯数学场,i了

//1 0 0 0 1 0 0 0 为什么是10?
//init +1
//第二个1---->6
//第一个1---->3
//sum

 

posted @ 2022-07-18 10:43  cf不上1500不改名  阅读(21)  评论(0)    收藏  举报