题解: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;
}

posted @ 2026-06-05 11:14  Wxb2010  阅读(6)  评论(0)    收藏  举报