CF1093F Vasya and Array

CF1093F Vasya and Array

CF1093F 加强版。

Niko 有一个长度为 nn 的整数序列 aa 以及两个整数 k,mk,\text{m},保证 ai∈[1,k]∪{−1}a_i\in[1,k]\cup\{-1\}。

求有多少种将 aa 中的 −1-1 替换为 [1,k][1,k] 中的整数的方式,使得 aa 中不存在长度为 m\text{m} 的连续相同区间。答案对 109+710^9+7 取模。

n,k,m≤2×106n,k,m\le 2\times 10^6。

原版支持 O(nk)\mathcal{O}(nk),加强版只能 O(n)\mathcal{O}(n)。

先看简单版(虽然没有什么用处):

定义 fi,jf_{i,j} 表示前 ii 个以 jj 结尾的方案数,那么我们只需要枚举连续的 jj 的个数就行:

fi,j=∑k(∑pfk,p)−fk,jf_{i,j}=\sum_k(\sum_p f_{k,p})-f_{k,j}

没了。

令 fif_i 表示前 ii 个的方案数,bib_i 表示 ii 之前第一个 ai≠−1a_i\ne -1 的位置,lil_i 表示 ii 之前第一个 ≠−1\ne -1 且 ≠abi\ne a_{b_i} 的位置。W(i,j)W(i,j) 表示在 ii 到 jj 之间填充同一个数的方案数,FiF_i 表示 ii 的容斥系数,则:

W(i,j)={ki>bi1bi≥i>li0li≥iW(i,j)=\begin{cases}k&i>b_i\\ 1&b_i\ge i>l_i\\ 0&l_i\ge i \end{cases}

且:

fi=∑jfjW(j,i)Fi−j+1f_i=\sum_j f_jW(j,i)F_{i-j+1}

W(i,j)W(i,j) 通过 bi,lib_i,l_i 和合适的分类讨论可以轻松解决,所以我们的主要问题就是如何处理或求出 FjF_j。

令 F(x)F(x) 表示 FF 的生成函数,则由于我们知道,若干个颜色相同的连续段拼在一起后,他们的必须为 <m<m 是 11,≥m\ge m 是 00。因此有:

11−F(x)=1−xm1−x\frac{1}{1-F(x)}=\frac{1-x^m}{1-x}

解得:对于 L≡1(modm)L\equiv 1\pmod m,FL=1F_L=1;L≡0(modm)L\equiv0\pmod m,FL=−1F_L=-1;其余为 00。

因此就很简单了,只需要令 gg 表示同余意义下的前缀和,就可以十分简单的写出了。复杂度 O(n)\mathcal{O}(n)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <bits/stdc++.h>
using namespace std;
const int N=2000010,P=1000000007;
int n,K,m,a[N],f[N],g[N],b[N],l[N];
int main(){
scanf("%d%d%d",&n,&K,&m);
for(int i=1;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=n;i++){
b[i]=(a[i]<0?b[i-1]:i);
l[i]=(a[i]<0||a[i]==a[b[i-1]]?l[i-1]:b[i-1]);
}
auto L=[&](int x,int y){return x-(x-y%m+m)%m;};
f[0]=g[0]=1;
for(int i=1;i<=n;i++){
auto calc=[&](int t,int c){
int x1=L(i-1,t),x2=L(b[i]-1,t),x3=L(l[i]-1,t);
int A=(x1<0?0:g[x1]),B=(x2<0?0:g[x2]),C=(x3<0?0:g[x3]);
int t1=1ll*K*(A-B+P)%P,t2=(B-C+P)%P,t3=(t1+t2)%P;
(f[i]+=(c==-1?P-t3:t3))%=P;
};
calc(i-1,1),calc(i,-1);
g[i]=(f[i]+(i>=m?g[i-m]:0))%P;
}
printf("%d",f[n]);
return 0;
}