题解 序列
- 注意形如「求包含一个给定子串(出现位置可以重叠)」的限制中那个「可以重叠」的条件可以这样去掉:
枚举这个子串的出现位置,剩下的位置任意填
这样每种合法方案都是会被计算到的 - 一个有些经典的问题:
部分分暴力DP的话可以利用 的限制
然后考虑原问题
发现现在不会加上必须是colorful sequence的限制
于是容斥减掉不是的
若给定序列是colorful的不用减
若给定序列元素distinct可以DP:
发现并不关心具体内容,所以尝试DP出在长为 的序列中长为 的distinct序列数量
额外令一个 为这个东西,转移与上面基本相同,在 时 就行了
若有重复元素可以取出distinct的前缀和后缀分别DP,乘法原理合并即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 25010
#define ll long long
// #define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k, m;
int a[N], buc[N];
const ll mod=1e9+7;
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}
bool iscolorful() {
if (m<k) return 0;
int cnt=0;
for (int i=1; i<=k; ++i) if (!buc[a[i]]++) ++cnt;
if (cnt==k) return 1;
for (int i=k+1; i<=m; ++i) {
if (!buc[a[i]]++) ++cnt;
if (!(--buc[a[i-k]])) --cnt;
if (cnt==k) return 1;
}
return 0;
}
bool distinct() {
for (int i=1; i<=k; ++i) buc[i]=0;
for (int i=1; i<=m; ++i) if (buc[a[i]]++) return 0;
return 1;
}
namespace task1{
ll f[N][410], g[N][410], fac[N], inv[N], ans;
ll solve() {
f[0][0]=1; fac[0]=fac[1]=1; inv[0]=inv[1]=1;
for (int i=2; i<=k; ++i) fac[i]=fac[i-1]*i%mod;
for (int i=2; i<=k; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for (int i=2; i<=k; ++i) inv[i]=inv[i-1]*inv[i]%mod;
f[0][0]=1;
for (int i=1; i<=n; ++i) {
ll sum=0, sum2=0;
for (int j=k-1; j; --j) {
sum=(sum+f[i-1][j])%mod;
sum2=(sum2+g[i-1][j])%mod;
f[i][j]=((k-j+1)*f[i-1][j-1]+sum)%mod;
g[i][j]=((k-j+1)*g[i-1][j-1]+sum2+(j>=m?f[i][j]:0))%mod;
}
}
for (int i=1; i<k; ++i) ans=(ans+g[n][i])%mod;
return ans*fac[k-m]%mod*inv[k]%mod;
}
}
namespace task2{
int cnt[N], len1, len2;
ll f[N][410], g[N][410], ans;
ll solve() {
for (int i=1; i<=k; ++i) cnt[i]=0;
for (len1=1; !cnt[a[len1]]; ++cnt[a[len1++]]) ;
for (int i=1; i<=k; ++i) cnt[i]=0;
for (len2=m; !cnt[a[len2]]; ++cnt[a[len2--]]) ;
--len1; len2=m-len2;
// cout<<"len: "<<len1<<' '<<len2<<endl;
f[0][len1]=g[0][len2]=1;
for (int i=1; i<=n; ++i) {
ll sum1=0, sum2=0;
for (int j=k-1; j; --j) {
sum1=(sum1+f[i-1][j])%mod;
sum2=(sum2+g[i-1][j])%mod;
f[i][j]=((k-j+1)*f[i-1][j-1]+sum1)%mod;
g[i][j]=((k-j+1)*g[i-1][j-1]+sum2)%mod;
}
}
for (int i=0; i<=n-m; ++i) {
ll t1=0, t2=0;
for (int j=1; j<k; ++j) t1=(t1+f[i][j])%mod;
for (int j=1; j<k; ++j) t2=(t2+g[n-m-i][j])%mod;
ans=(ans+t1*t2)%mod;
}
// cout<<ans<<endl;
return ans;
}
}
signed main()
{
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
n=read(); k=read(); m=read();
for (int i=1; i<=m; ++i) a[i]=read();
if (iscolorful()) printf("%lld\n", ((1ll*(n-m+1)*qpow(k, n-m))%mod+mod)%mod);
else if (distinct()) printf("%lld\n", ((1ll*(n-m+1)*qpow(k, n-m)-task1::solve())%mod+mod)%mod);
else printf("%lld\n", ((1ll*(n-m+1)*qpow(k, n-m)-task2::solve())%mod+mod)%mod);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】