[DP记录]AT4169 [ARC100D] Colorful Sequences

command_block

2021-05-04 15:46:46

Personal

**题意** : 给定一个长为 $m$ 的序列 $A$,保证每个数都 $\leq k$。 如果一个全部元素均 $\leq x$ 的序列中存在一个长度为 $x$ 连续区间,恰好是 $1\sim x$ 的排列,那么称这个序列为 「 $x$−好序列 」。 给定长度 $n$ 。求所有长度为 $n$ 的 「 $k$−好序列 」 共包含了多少个 $A$ (允许重叠)。 $n\leq 2.5\times 10^4,m\leq 400$ ,时限$\texttt{2s}$。 ------------ 发现对好序列计数较为困难(条件是或起来的),考虑取反,先统计在所有序列中 $A$ 的出现次数,再减去不好序列中 $A$ 的出现次数。 前者容易得到 : 枚举 $A$ 的起点,确定了接下来的 $m$ 个,其余位置随意。为 $k^{n-m}(n-m+1)$。 下面是后者的计算。 - Case 1 : $A$ 本身是好序列。 包含 $A$ 的序列必然是好序列,答案为 $0$。 - Case 2 : $A$ 中没有重复的数。 由于没有重复的数,各个颜色之间并没有本质区别。 $A$ 为所有 “$m$ 个不同 $[1,k]$ 内数组成的序列” 的答案都是相同的。 于是,统计它们的和,并将答案除以 $k^{\underline m}$。 (定义一个区间是好的当且仅当其中没有重复的数) 记 $f[i,j]$ 表示填写了 $i$ 个数,且极长好后缀的长度为 $j$ 的方案数。 有转移 : $$ \begin{matrix} f[i,j]\rightarrow f[i+1,k]&(1\leq k\leq j)\\ (k-j)f[i,j]\rightarrow f[i+1,j+1] \end{matrix} $$ 第一条 : 放一个和倒数第 $k$ 个重复的数。 第二条 : 放一个前 $j$ 个没有出现过的数。这个数有 $k-j$ 种可能。 记 $g[i,j]$ 表示填写了 $i$ 个数,且极长好后缀的长度为 $j$ ,含有的长度为 $m$ 的好区间总数。 转移和 $f$ 相同,在 $j\geq m$ 时额外加上 $f[i,j]$。 答案即为 $\sum g[n,\_]/k^{\underline m}$。 - Case 2 : $A$ 中有重复的数。 记 $A$ 极长好前后缀的长度分别为 $L,R$。 枚举 $A$ 放置的位置的左端点 $p$ ,从 $A$ 分别向左向右考虑。 对于左侧,令 $f[0][L]=1$ ,然后 $\rm DP$ , $\sum f[p-1][\_]$ 即为左侧的方案数。 对于右侧,令 $f[0][R]=1$ ,然后 $\rm DP$ , $\sum f[n-m-p+1][\_]$ 即为右侧的方案数。 上述 $\rm DP$ 容易用前缀和优化做到 $O(nm)$ 。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 25050 #define MaxK 405 using namespace std; const int mod=1000000007; ll powM(ll a,int t=mod-2){ ll ret=1; while(t){ if (t&1)ret=ret*a%mod; a=a*a%mod;t>>=1; }return ret; } int f[MaxN][MaxK],g[MaxN][MaxK]; int n,m,k,x[MaxN],e[MaxK],ans; void solve1() { f[0][0]=1;f[1][1]=k; for (int i=1;i<=n;i++){ for (int j=1;j<k;j++){ f[i][j]=(f[i][j]+f[i-1][j])%mod; f[i+1][j+1]=1ll*(k-j)*f[i][j]%mod; g[i][j]=(g[i][j]+g[i-1][j])%mod; if (j>=m)g[i][j]=(g[i][j]+f[i][j])%mod; g[i+1][j+1]=1ll*(k-j)*g[i][j]%mod; } f[i][k]=g[i][k]=0; for (int j=k-1;j;j--){ f[i][j]=(f[i][j]+f[i][j+1])%mod; g[i][j]=(g[i][j]+g[i][j+1])%mod; } } int buf=1; for (int i=0;i<m;i++)buf=1ll*buf*(k-i)%mod; ans=1ll*powM(buf)*g[n][1]%mod; } void solve2() { int L=1,R=m; for (int i=1;i<=k;i++)e[i]=0; while(!e[x[L]])e[x[L++]]=1;L--; for (int i=1;i<=k;i++)e[i]=0; while(!e[x[R]])e[x[R--]]=1;R++; R=m-R+1; for (int i=0;i<=L;i++)f[0][i]=1; for (int i=0;i<=R;i++)g[0][i]=1; f[1][L+1]=k-L;g[1][R+1]=k-R; for (int i=1;i<=n-m;i++){ for (int j=1;j<k;j++){ f[i][j]=(f[i][j]+f[i-1][j])%mod; f[i+1][j+1]=1ll*(k-j)*f[i][j]%mod; g[i][j]=(g[i][j]+g[i-1][j])%mod; g[i+1][j+1]=1ll*(k-j)*g[i][j]%mod; } f[i][k]=g[i][k]=0; for (int j=k-1;j;j--){ f[i][j]=(f[i][j]+f[i][j+1])%mod; g[i][j]=(g[i][j]+g[i][j+1])%mod; } } for (int p=1;p<=n-m+1;p++) ans=(ans+1ll*f[p-1][1]*g[n-m-p+1][1])%mod; } int main() { scanf("%d%d%d",&n,&k,&m); if (k>n){puts("0");return 0;} bool fl1=0,fl2=0; for (int i=1,tl=0;i<=m;i++){ scanf("%d",&x[i]); tl=max(tl,e[x[i]]);e[x[i]]=i; if (i-tl==k)fl1=1; if (tl>0)fl2=1; } if (!fl1&&k<=n){ if (!fl2)solve1(); else solve2(); } int buf=powM(k,n-m)*(n-m+1)%mod; printf("%d",(buf-ans+mod)%mod); return 0; } ```