题解:P11655 「FAOI-R5」Lovely 139
题解:P11655 「FAOI-R5」Lovely 139
前置知识
题目思路
对于一个 \(\tt 01\) 串 \(S\)(下标从 \(1\) 开始),我们定义它的一个区间 \([l,r]\) 是极长颜色段,当且仅当它同时满足以下条件:
- 如果 \(l\neq 1\),\(S_{l-1}\neq S_l\);
- 如果 \(r\neq \lvert S\rvert\),\(S_{r+1}\neq S_r\);
- \(\forall i\in[l,r),S_i=S_{i+1}\)。
观察上述条件,不难发现一个性质,即对于一个字符串 \(S\),两个相邻的字符不同的数量加上 \(1\) 就是 \(S\) 的不同极长颜色段数。即:
所以,问题就分为了两个部分:
- 计算所有满足条件的相邻两个字符之和
- 以及这些字符串的总数。(即通过排列组合生成的所有字符串所加的 \(1\) 之和)
计算字符串的总数
显然,这是一个排列组合问题,结果即为 \(C_{m+n}^n\) 或 \(C_{m+n}^m\),实际上两者的结果都是一样的,读者可以自己赋特殊值进行证明。所以通过排列组合生成的所有字符串所加的 \(1\) 之和也就是 \(C_{m+n}^n\) 或 \(C_{m+n}^m\) 了。
计算所有满足条件的相邻两个字符之和
对于每一组 \(S_i,S_{i+1}(S_i \neq S_{i+1})\),则还有 \(n-1\) 个 \(0\),\(m-1\) 个 \(1\),\(n+m-2\) 个 \(0\) 和 \(1\)。则对于其他子串的排列方案为 \(C_{m+n-2}^{n-1}\) 或 \(C_{m+n-2}^{m-1}\)。同理,两者结果仍相同。又因为对于每一组 \(S_i,S_{i+1}(S_i \neq S_{i+1})\),有 \(\tt 01\) 和 \(\tt 10\) 两种情况,并且 \(S\) 中有 \(n+m-1\) 组这样的数,所以所有满足条件的相邻两个字符之和为:
所以:
代码实现
0pts Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod=1e9+7;
ll jc(ll a){
ll sum=1;
for(int i=1;i<=a;i++){
sum=sum*i;
}
return sum;
}
ll zh(ll a,ll b){return jc(b)/(jc(a)*jc(b-a));}
int main(){
ll T,n,m;
cin>>T;
while(T--){
cin>>n>>m;
cout<<(zh(n,n+m)+2*(n+m-1)*zh(n-1,n+m-2))%mod<<"\n";
}
return 0;
}
一开始推得规律后,就直接写了个阶乘和组合函数,但显然不行,还要处理对 \(10^{9}+7\) 取模的问题。再到后来甚至想使用高精乘,但仍无法处理对 \(10^{9}+7\) 取模的问题。
于是想到了P3811 【模板】模意义下的乘法逆元和B3717 组合数问题,知道是乘法逆元,于是套上了板子。
AC Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const ll MAXN=2e6+10;
ll f[MAXN],inv[MAXN];
inline ll read() {
ll x = 0, f = 1; char ch = getchar();
while(ch>'9'||ch<'0') { if(ch=='-') f=-f;ch=getchar(); }
while(ch>='0'&&ch<='9') { x=x*10+ch-'0';ch=getchar(); }
return x*f;
}
ll zh(ll n,ll m){
return f[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll T,n,m;
T=read();
f[1]=f[0]=inv[1]=inv[0]=1;
for(ll i=2;i<=MAXN;i++){
f[i]=f[i-1]*i%mod;
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
for(ll i=1;i<=MAXN;i++) inv[i]=inv[i-1]*inv[i]%mod;
for(ll i=1;i<=T;i++){
n=read(),m=read();
if(n==0&&m==0) cout<<1<<"\n";
else if(n==0||m==0) cout<<1<<"\n";
else{
cout<<((n+m-1)*2*(zh(n+m-2,n-1)%mod)%mod+(zh(n+m,n)%mod))%mod<<"\n";
}
}
return 0;
}