例题一:P7322 「PMOI-4」排列变换
「PMOI-4」排列变换 - 洛谷https://www.luogu.com.cn/problem/P7322
解法:
问滑动窗口中的 max 变化了多少次。
因为向右滑动 1长度的情况下,只有窗口最左的数会出窗口,而右边紧邻的那个数会进入窗口,所以我们实际上只需要对于每个窗口看最左边的数是否是窗口滑动之前的最大值,以及新加入的最右边的数是否是窗口滑动之后新的最大值即可。
(也可以用最小值来理解:)
其中从左往右,求和符合是枚举这个最小值的大小,组合数是令窗口中剩余的 k−1 个数大于最小值的值的个数,两个阶乘分别是窗口外面的数任意排列和窗口内部除去最小值外任意排列,最后一个 n−k 是计算排列中窗口可以在的位置有 n−k 个。(其实总窗口有n−k+1 个,就是最左边那个无法移动了)。注意:左边和右边对应的都是这个式子,*2即可
实际上我们还会算重一部分,既满足最左边是窗口最小值,又满足新加入的是窗口的新最小值,这部分答案需要减掉:考虑左边小右边大、左边大右边小两种情况:
所以请看代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 5e5+5,mod = 998244353;
ll n,k,inv[N],fac[N],ifac[N];
inline ll Mod(ll x) {return x>=mod?x-mod:x;}
inline void init()
{
inv[1]=fac[0]=ifac[0]=1;
for(int i=1;i<=n;++i)
{
if(i^1) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
fac[i]=fac[i-1]*i%mod;
ifac[i]=ifac[i-1]*inv[i]%mod;
}
}
inline ll C(ll x,ll y)
{
if(x<y) return 0;
return fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}
int main()
{
scanf("%lld %lld",&n,&k);
init();
ll ans=0;
for(ll i=0;i<=n-1;++i)
{
ll tmp=2*(n-k)*fac[n-k]%mod*fac[k-1]%mod*C(i,k-1)%mod;
ans=(ans+tmp)%mod;
}
for(ll i=0;i<=n-1;++i)
{
ll tmp2=2*(n-k)*fac[n-k-1]%mod*fac[k-1]%mod*C(i,k)%mod;
ans=((ans-tmp2)%mod+mod)%mod;
}
ans=(ans+fac[n])%mod;
ans=(ans%mod+mod)%mod;
printf("%lld",ans);
return 0;
}
例题二:P7044 「MCOI-03」括号
「MCOI-03」括号 - 洛谷https://www.luogu.com.cn/problem/P7044
- K=0
就是经典括号匹配问题,开个栈模拟一下即可。
- K=1
显然可以 O(n2) 枚举子串,但这样复杂度太高。
考虑拆开找贡献,对于下标为 i 的左括号,设它匹配的右括号下标为 j (特别的,没有匹配时 j=n+1 )。
那么易证,这个左括号对所有i≤r<j 且 1≤l≤i 的区间都有 1 的贡献,总贡献即为 i×(j−i) 。
右括号同理,因此可以O(n) 计算。
- K=2(层数再多的时候同理)
请看代码:
#include<bits/stdc++.h>
#define MOD 998244353
#define N 2000010
using namespace std;
typedef long long ll;
ll n,k,ans;
ll f[N],inv[N],finv[N],p[N];
char ch[N];
stack<ll> s;
ll C(ll n,ll m)
{
return f[n]*finv[m]%MOD*finv[n-m]%MOD;
}
int main()
{
f[0]=f[1]=inv[0]=inv[1]=finv[0]=finv[1]=1;
for(ll i=2;i<=2000000;++i)
f[i]=f[i-1]*i%MOD;
for(ll i=2;i<=2000000;++i)
{
inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
finv[i]=finv[i-1]*inv[i]%MOD;
}
scanf("%lld %lld",&n,&k);
scanf("%s",ch+1);
for(ll i=1;i<=n;++i)
if(ch[i]=='(')
p[i]=n+1;
for(ll i=1;i<=n;++i)
if(ch[i]=='(')
s.push(i);
else
{
if(s.empty())
continue;
p[s.top()]=i;
p[i]=s.top(),s.pop();
}
for(ll i=1;i<=n;++i)
if(ch[i]=='(')
ans=(ans+C(k+i-1,k)*(C(k+n-i,k)-C(n-p[i]+k,k)+MOD)%MOD)%MOD;
else
ans=(ans+C(k+n-i,k)*(C(k+i-1,k)-C(k+p[i]-1,k)+MOD)%MOD)%MOD;
printf("%lld\n",ans);
return 0;
}
例题三:P5505 [JSOI2011] 分特产
[JSOI2011] 分特产 - 洛谷https://www.luogu.com.cn/problem/P5505
考虑两个情况:1. 恰好x个人没特产 2. 至少x个人没特产,看哪个好求,这题中很明显应该是第二个,且第一个可以通过第二个容斥加减得到。
现在求出了至少i个人没特产的情况就可以着手去求恰好的时候了:
代码如下:
#include<bits/stdc++.h>
#define MOD 1000000007
#define N 2010
using namespace std;
typedef long long ll;
ll ans;
ll f[N],inv[N],finv[N],F[N],a[N];
char ch[N];
stack<ll> s;
ll qpow(ll x,ll k){
ll res=1;
while(k){
if(k&1) res=res*x%MOD;
x=x*x%MOD;k>>=1;
}
return res;
}
ll C(ll n,ll m)
{
return f[n]*finv[m]%MOD*finv[n-m]%MOD;
}
int main()
{
ll n,m;
scanf("%lld %lld",&n,&m);
for(ll i=1;i<=m;++i)
scanf("%lld",&a[i]);
f[0]=f[1]=inv[0]=inv[1]=finv[0]=finv[1]=1;
for(ll i=2;i<=2010;++i)
f[i]=f[i-1]*i%MOD;
for(ll i=2;i<=2010;++i)
{
inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
finv[i]=finv[i-1]*inv[i]%MOD;
}
for(ll i=0;i<=n;++i)
{
F[i]=C(n,i);
for(ll j=1;j<=m;++j)
{
F[i]=F[i]*C(a[j]+n-i-1,a[j])%MOD;
}
}
for(ll i=0;i<=n-1;++i)
{
if(i&1)
{
ans-=F[i];
ans=(ans%MOD+MOD)%MOD;
}
else
{
ans+=F[i];
ans%=MOD;
}
}
printf("%lld",ans);
return 0;
}
例题四:P6076 [JSOI2015] 染色问题
[JSOI2015] 染色问题 - 洛谷https://www.luogu.com.cn/problem/P6076转载:洛谷题解
我的代码:
#include<bits/stdc++.h>
#define MOD 1000000007
#define N 1010
using namespace std;
typedef long long ll;
ll ans;
ll f[N],inv[N],finv[N],F[N];
char ch[N];
stack<ll> s;
ll qpow(ll x,ll k){
ll res=1;
while(k){
if(k&1) res=res*x%MOD;
x=x*x%MOD;k>>=1;
}
return res;
}
ll C(ll n,ll m)
{
return f[n]*finv[m]%MOD*finv[n-m]%MOD;
}
int main()
{
ll n,m,c;
scanf("%lld %lld %lld",&n,&m,&c);
f[0]=f[1]=inv[0]=inv[1]=finv[0]=finv[1]=1;
for(ll i=2;i<=1010;++i)
f[i]=f[i-1]*i%MOD;
for(ll i=2;i<=1010;++i)
{
inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
finv[i]=finv[i-1]*inv[i]%MOD;
}
for(ll i=1;i<=c;++i)
{
ll tmp=0;
for(ll j=1;j<=m;++j)
{
if((m-j)&1)
{
tmp=((tmp-C(m,j)*qpow(qpow(i+1,j)-1,n))%MOD+MOD)%MOD;
}
else
{
tmp=((tmp+C(m,j)*qpow(qpow(i+1,j)-1,n))%MOD+MOD)%MOD;
}
}
F[i]=tmp;
}
ll tmp=0;
for(ll i=0;i<=c;++i)
{
if((c-i)&1)
{
ans-=(F[i]*C(c,i));
ans=(ans%MOD+MOD)%MOD;
}
else
{
ans+=(F[i]*C(c,i))%MOD;
ans%=MOD;
}
}
printf("%lld",ans);
return 0;
}