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

浙公网安备 33010602011771号