题解 【P9149 串串题】

· · 题解

感觉不如上一题有意思。

题解

容易发现,计算所有方案的贡献之和,可以转化为计算每个可能的匹配的位置,在所有方案下匹配上 B 的次数之和。假定 A 数组里,原来在 [l,r] 位置的数字在删数之后变成了一个 Bl 位置和 r 位置上的数字没有被删)。

容易注意到两条关键性质:

考虑枚举 l 的值。一个 l 是合法的,当且仅当在删除所有不在 B 里的元素后,[l,r] 区间剩下来的元素恰好是 B。那么可以先把所有不在 B 里的元素都删掉,然后跑一下 \text{KMP},就知道哪些 l 是合法的了。顺便可以知道 r 的值。

然后就是统计 [l,r] 区间出现了多少种不在 B 里的元素。记这个种类数为 a。同时我们记 B 里面出现的元素种类数为 b

那么,[l,r] 里出现的不在 B 里的元素肯定要被删掉,B 里出现的元素肯定不能被删,剩下来的数字可删可不删。也就是说,我们要从剩下的这 W-a-b 个数里选择 d-a 个数字删掉。

所以这个位置 l 对答案的贡献就是 \dbinom{W-a-b}{d-a}。计算所有 l 的贡献之和即可。

整个的时间复杂度容易做到 $\mathcal O(W+\sum n_i)$。但是我太懒了没写线性求逆元,写了个 $\mathcal O(W\log W+\sum n_i)$ 的代码。 ## 参考代码 ```cpp #include<bits/stdc++.h> #define up(l, r, i) for(int i = l, END##i = r;i <= END##i;++ i) #define dn(r, l, i) for(int i = r, END##i = l;i >= END##i;-- i) using namespace std; typedef long long i64; const int INF = 2147483647; const int MAXN= 1e6 + 3; const int MOD = 1e9 + 7; int P[MAXN], Q[MAXN], o = 1e6; int power(int a, int b){ int r = 1; while(b){ if(b & 1)r = 1ll * r * a % MOD; b >>= 1, a = 1ll * a * a % MOD; } return r; } int choose(int a, int b){ if(a < 0 || b < 0 || a - b < 0) return 0; return 1ll * P[a] * Q[b] % MOD * Q[a - b] % MOD; } int qread(){ int w=1,c,ret; while((c = getchar()) > '9' || c < '0') w = (c == '-' ? -1 : 1); ret = c - '0'; while((c = getchar()) >= '0' && c <= '9') ret = ret * 10 + c - '0'; return ret * w; } int n, m, w, d; int A[MAXN], B[MAXN], C[MAXN], D[MAXN], N[MAXN], T[MAXN]; int X[MAXN], Y[MAXN]; int main(){ P[0] = Q[0] = 1; up(1, o, i) P[i] = 1ll * P[i - 1] * i % MOD, Q[i] = power(P[i], MOD - 2); up(1, qread(), _){ n = qread(), m = qread(), w = qread(), d = qread(); int t = 0, val = 0, res = 0; up(1, n, i) A[i] = qread(); up(1, m, i) B[i] = qread(), val += !X[B[i]], X[B[i]] = true; up(1, n, i) C[i] = D[i] = N[i] = T[i] = 0; up(1, n, i) if(X[A[i]]) C[++ t] = A[i], D[t] = i; N[1] = 0, A[n + 1] = B[m + 1] = 0; up(2, m, i){ int p = N[i - 1]; while(p != 0 && B[p + 1] != B[i]) p = N[p]; if(B[p + 1] == B[i]) N[i] = p + 1; else N[i] = 0; } up(1, t, i){ int p = T[i - 1]; while(p != 0 && B[p + 1] != C[i]) p = N[p]; if(B[p + 1] == C[i]) T[i] = p + 1; else T[i] = 0; } int l = 1, r = 0, ans = 0; up(1, t, i) if(T[i] == m){ int y = D[i], x = D[i - m + 1]; while(r < y){ ++ r; if(!X[A[r]] && Y[A[r]] == 0) ++ ans; if(!X[A[r]]) Y[A[r]] ++; } while(l < x){ if(!X[A[l]]) -- Y[A[l]]; if(!X[A[l]] && Y[A[l]] == 0) -- ans; ++ l; } res = (res + choose(w - val - ans, d - ans)) % MOD; } up(1, m, i) X[B[i]] = 0; up(1, n, i) Y[A[i]] = 0; printf("%d\n", res); } return 0; } ```