题解:AT_arc100_d [ARC100F] Colorful Sequences
前言
本文同步自洛谷专栏,题目传送门。
提供一个不需要复杂容斥,直接计数的方法。
题面中的连续子序列改称子区间。
解题
初步观察
先判掉 \(K>n\) 的情况。
我们需要统计包含与 \(A\) 完全相同的子区间的个数,并求这些个数的总和,也就是说,最终的彩色序列中,包含了 \(A\) 多次要统计多次,可以考虑去枚举 \(A\) 出现的位置。
针对彩色的限制,定义一些内容。
称一个区间是不重区间,当且仅当区间内每个数出现至多一次。
称一个区间是极长不重区间,当且仅当区间是不重区间,且左右边界不能扩展或扩展后不为不重区间。
称一个区间是彩色区间,当且仅当其有一个极长不重区间长度为 \(K\)。
类似的内容可以参考以上定义。
还有一个观察是,对于每个位置,不需要关心具体的数值,只需要保留相等和不等关系即可。
分析
对 \(A\) 统计它的极长不重区间长度的最大值、极长不重前缀长度、极长不重后缀长度,分别记作 \(maxn,pre,suf\)。
如果 \(maxn=K\),则只要包含 \(A\),即是彩色序列,答案为 \((n-m+1)\times K^{n-m}\),分别表示枚举 \(A\) 的位置、其它位置的填法。
如果 \(maxn=m\),即整个 \(A\) 序列是一个不重序列,那么最终的彩色序列的情况较为复杂,放在最后讨论。
情况一
现在考虑其它情况。
假设现在 \(A\) 序列处于 \([l,r]\) 处,那么所有情况一定可以被划分为两类:
\([l,n]\) 是彩色区间,\([l,n]\) 不是彩色区间且 \([1,r]\) 是彩色区间。
考虑前者,我们需要统计在一个长度为 \(suf\) 的极长不重区间后拼接 \(n-r\) 个数,得到一个彩色序列的方案数。
这个是可以 dp 求解的,考虑设 \(dp_{i,j}\) 表示在一个长度为 \(j\) 的极长不重区间后拼接 \(i\) 个数,得到一个彩色序列的方案数。
注意这里的状态设计类似期望 dp,这种对所有方案计数和期望是相关的。
那么有:转移 \(dp_{i,j}\leftarrow (K-j)\times dp_{i-1,j+1}\),表示接的数和原来的不同;
转移 \(dp_{i,j}\leftarrow dp_{i-1,x}(1\le x\le j)\),分别表示接的数和原来某个位置的数相同,可以应用前缀和优化转移做到 \(O(1)\)。
特别的,\(dp_{i,K}=K^i\),因为已经是一个彩色序列了。
上面这个 dp 时空复杂度都是 \(O(nK)\) 的。
前者的贡献显然为 \(dp_{n-r,suf}\times dp_{l-1,K}\),分别表示 \((r,n],[1,l)\) 部分的方案选择。
后者的贡献可以直接写出:\((dp_{n-r,K}-dp_{n-r,suf})\times dp_{l-1,pre}\),前一项为 \([l,n]\) 不是彩色区间的方案数,后一项即为 \([1,r]\) 是彩色区间的方案数。
$\red{\text{code}}$
void work2(){//可以枚举 A 的所处的位置
ull b,c;//A 前后两部分相对独立
for(int l=1,r=m;r<=n;l++,r++){//c 表示未成功的方案数
b=dp[n-r][suf],c=dp[n-r][K]+mod-b;
ans=(ans+c*dp[l-1][pre]+b*dp[l-1][K])%mod;
}
}
情况二
再来看 \(maxn=m\) 的情况,我们借鉴情况一的处理方法,尝试让 \(A\) 的左右两侧相对独立。
设 \(A\) 序列现在在 \([l,r]\) 处,枚举向后 \(p+1\) 位,使 \([l,r+p]\) 是不重区间但 \([l,r+p+1]\) 不是不重区间(有限制 \(m+p\le K,r+p+1\le n\))。
此时,\([l,r+p+1]\) 的极长不重前缀长度 \(pre=m+p\),极长不重后缀长度 \(suf\) 取遍 \([1,m+p]\),仿照情况一进行计数,
$\red{\text{注}}$
极长不重后缀长度 \(suf\) 取遍 \([1,m+p]\) 的由来:
\(r+p+1\) 位与 \([l,r+p]\) 中的每一位分别相等时,对应 \(suf\) 的长度从 \(m+p\) 降到 \(1\),这里不用考虑具体取值,只需保留相等和不等关系。
\([1,r]\) 是彩色区间的方案数:\(dp_{l-1,pre}\times dp_{n-r-p-1,K}\times(m+p)\),后两项分别代表 \((r+p+1,n],r+p+1\) 上的方案数(注意 \(r+p+1\) 上的方案为 \(m+p\) 而非 \(K\),否则 \(pre\ne m+p\))。
\([1,r]\) 不是彩色区间但 \([l,n]\) 是彩色区间的方案数为:
\(\sum_{1\le x\le m+p}(dp_{l-1,K}-dp_{l-1,pre})\times dp_{n-r-p-1,x}\),表示枚举 \(suf\) 取值得到的答案,\(\sum_{1\le x\le m+p}dp_{n-r-p-1,x}\) 容易前缀和优化。
还要补上 \((r,r+p]\) 部分的方案数:\(p!\binom{K-m}{p}\),即选 \(p\) 个和 \([l,r]\) 中不同的数排列的方案数。
在实现的时候,\(m+p=K,r+p=n\) 的情况需要特殊处理,因为前者表示直接得到一个长为 \(K\) 的彩色区间,后者表示已经达到序列末尾,具体处理可以参考代码。
实现
预处理的 dp 复杂度为 \(O(nK)\),情况一的统计是线性的,情况二枚举 \(p\) 的过程是 \(O(K)\) 的,总时间复杂度 \(O(nK)\),空间也是 \(O(nK)\) 的。
$\red{\text{code}}$
#include<bits/stdc++.h>
using namespace std;
#define N 25005
#define ull unsigned long long
#define mod 1000000007
int dp[N][405],cnt[405],sum[N][405];//剩余 i 个位置,最大不重长度为 j,能形成彩色序列的方案数
int n,K,m,pre,suf,maxn,A[N];//dp 数组是对任何一个确定的序列成立
ull fac[N],inv[N],ans;
inline void inc(int &a,int b){a+=b,(a>=mod)&&(a-=mod);}
inline ull ksm(ull a,int b){
ull ret=1;
for(;b;b>>=1,a=a*a%mod){
if(b&1) ret=ret*a%mod;
}
return ret;
}
void init(){//注意 dp[i][K] 还可以表示 i 个位置任意放的方案数
for(pre=1;!cnt[A[pre]]&&pre<=m;pre++) cnt[A[pre]]=1;
memset(cnt,0,sizeof(cnt));
for(suf=m;!cnt[A[suf]]&&suf>=1;suf--) cnt[A[suf]]=1;
memset(cnt,0,sizeof(cnt)),suf=m-suf,pre--,dp[0][K]=1;
for(int r=1,l=1;r<=m;r++){//pre,suf 表示 A 前后缀中最长的不重复部分
while(cnt[A[r]]) cnt[A[l]]--,l++;
maxn=max(maxn,r-l+1),cnt[A[r]]++;
}
for(int i=1,S=0;i<=n;i++){
for(int j=1;j<K;j++){
dp[i][j]=1ull*(K-j)*dp[i-1][j+1]%mod;
inc(S,dp[i-1][j]),inc(dp[i][j],S);
}
S=0,dp[i][K]=1ull*dp[i-1][K]*K%mod;//特殊处理
}
}
void work1(){
memset(cnt,0,sizeof(cnt)),fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
for(int i=0;i<=n;i++){
for(int j=1;j<K;j++){
sum[i][j]=sum[i][j-1],inc(sum[i][j],dp[i][j]);
}
}
inv[n]=ksm(fac[n],mod-2);
for(int i=n;i>=1;i--) inv[i-1]=inv[i]*i%mod;
ull b,c,d;
for(int l=1,r=m,p;r<=n;l++,r++){//枚举 m+p 作为不同的,最后一位作为阻断位
for(p=0;m+p<K&&r+p+1<=n;p++){
b=dp[l-1][m+p],c=dp[l-1][K]+mod-b;
d=(c*sum[n-r-p-1][m+p]+b*dp[n-r-p-1][K]%mod*(m+p))%mod;
ans=(ans+d*fac[K-m]%mod*inv[K-m-p])%mod;
}//下面要考虑 m+p=K 和 r+p=n 的情况
if(K-m==n-r){//向后放满且恰满足彩色条件
ans=(ans+dp[l-1][K]*fac[K-m])%mod;
}//[1,l) 位的总方案数 *(r,n] 的排列数
else if(r+(p=K-m)+1<=n){//向后到满足彩色条件
ans=(ans+dp[l-1][K]*fac[p]%mod*dp[n-r-p][K])%mod;
}//和上一个相比额外*一个 (r+p,n] 的方案数
else if(m+(p=n-r)<K){//向后放满
ans=(ans+dp[l-1][m+p]*fac[K-m]%mod*inv[K-m-p])%mod;
}//向前得到彩色序列的方案数*(r,n] 的方案数
}
}
void work2(){//可以枚举 A 的所处的位置
ull b,c;//A 前后两部分相对独立
for(int l=1,r=m;r<=n;l++,r++){//c 表示未成功的方案数
b=dp[n-r][suf],c=dp[n-r][K]+mod-b;
ans=(ans+c*dp[l-1][pre]+b*dp[l-1][K])%mod;
}
}
int main(){
scanf("%d%d%d",&n,&K,&m);
for(int i=1;i<=m;i++) scanf("%d",&A[i]);
if(K>n){printf("0");return 0;}//不可能的情况
init();
if(maxn==K) ans=1ull*(n-m+1)*dp[n-m][K]%mod;//完全覆盖的情况
else if(m==pre) work1();
else work2();
printf("%llu",ans);
return 0;
}

浙公网安备 33010602011771号