ARC100F Colorful Sequences

题目
因为直接计算每个符合要求的序列的权值比较困难(要求是或起来的),所以考虑算出所有序列的权值再减去不符合条件的序列权值。
所有序列的权值等于 k n − m × ( n − m + 1 ) k^{n-m}\times(n-m+1) knm×(nm+1)
考虑后者即不符合条件的序列的权值和怎么计算。
1. { a } \{a\} {a} 就是一个符合条件的序列
那么答案为 0 0 0
2. { a } \{a\} {a} 中元素不重复
可以注意到,既然 { a } \{a\} {a} 不重复,那么每个数没有本质区别,所以统计出 { a } \{a\} {a} 等于任意长度为 m m m 的元素不重复的序列时的答案再除以 k m ‾ k^{\underline m} km
f [ i ] [ j ] f[i][j] f[i][j] 表示填了 i i i 个数,最后 j j j 个数不重复的方案数
f [ i ] [ j ] ← ( k − j + 1 ) × f [ i − 1 ] [ j − 1 ] f[i][j]\leftarrow (k-j+1)\times f[i-1][j-1] f[i][j](kj+1)×f[i1][j1]
f [ i ] [ j ] ← f [ i − 1 ] [ k ] ( k ≥ j ) f[i][j]\leftarrow f[i-1][k](k\ge j) f[i][j]f[i1][k](kj)
g [ i ] [ j ] g[i][j] g[i][j] 表示填了 i i i 个数,最后 j j j 个数不重复的序列,其中含有长度为 m m m 的元素不重复的子串的个数之和。
g [ i ] [ j ] ← ( k − j + 1 ) × g [ i − 1 ] [ j − 1 ] g[i][j]\leftarrow (k-j+1)\times g[i-1][j-1] g[i][j](kj+1)×g[i1][j1]
g [ i ] [ j ] ← g [ i − 1 ] [ k ] ( k ≥ j ) g[i][j]\leftarrow g[i-1][k](k\ge j) g[i][j]g[i1][k](kj)
g [ i ] [ j ] + = f [ i ] [ j ] ( j ≥ m ) g[i][j]+=f[i][j](j\ge m) g[i][j]+=f[i][j](jm)
注意在枚举的时候 j < k j<k j<k 即可保证不存在合法的情况。
答案是 ∑ i = 1 k − 1 g [ n ] [ i ] k m ‾ \frac{\sum_{i=1}^{k-1}g[n][i]}{k^{\underline{m}}} kmi=1k1g[n][i]

3. { a } \{a\} {a} 中元素有重复
对于原串中出现的每一个 a a a 都分开统计答案,换言之,统计 a a a 出现 [ i , i + m − 1 ] [i,i+m-1] [i,i+m1] 区间时填原序列的方案数,答案即为前面每种情况方案数的总和。
L L L 表示极长前缀长度使得 a 1 , a 2 , ⋯   , a L a_1,a_2,\cdots,a_L a1,a2,,aL 互不相同。
R R R 表示极长后缀长度使得 a m − R + 1 , ⋯   , a m a_{m-R+1},\cdots,a_m amR+1,,am 互不相同。
f [ 0 ] [ L ] = 1 f[0][L]=1 f[0][L]=1
g [ 0 ] [ R ] = 1 g[0][R]=1 g[0][R]=1
然后 f f f g g g 都做一遍与前面第二种情况中 f f f 相同的转移就可以了。
答案是 ∑ i = 1 n − m + 1 ( ∑ j = 1 k − 1 f [ i − 1 ] [ j ] ) × ( ∑ j = 1 k − 1 g [ i − 1 ] [ j ] ) \sum_{i=1}^{n-m+1} (\sum_{j=1}^{k-1}f[i-1][j])\times(\sum_{j=1}^{k-1}g[i-1][j]) i=1nm+1(j=1k1f[i1][j])×(j=1k1g[i1][j])

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
const int N=25005;
const int M=405;
const int mod=1e9+7;
int n,k,m;
int a[N],inv[N],cnt[N],f[N][M],g[N][M],suf[N][M],suf1[N][M],suf2[N][M];
bool vis1[N],vis2[N];
inline int read()
{
	int s=0,t=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s*t;
}
inline int ksm(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1) res=1ll*res*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return res;
}
inline bool check()
{
	for(int i=1;i<=m;++i) ++cnt[a[i]];
	for(int i=1;i<=m;++i)
		if(cnt[a[i]]>1) return false;
	return true;
}
inline int calc1()
{
	if(m>=k) return 0;
	f[0][0]=1;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=k-1;++j)
		{
			f[i][j]=(f[i][j]+suf[i-1][j])%mod;
			f[i][j]=(f[i][j]+1ll*f[i-1][j-1]*(k-j+1)%mod)%mod;
		}
		for(int j=k-1;j;--j) suf[i][j]=(suf[i][j+1]+f[i][j])%mod;
	}
	memset(suf,0,sizeof(suf));
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=k-1;++j)
		{
			g[i][j]=(g[i][j]+suf[i-1][j])%mod;
			g[i][j]=(g[i][j]+1ll*g[i-1][j-1]*(k-j+1)%mod)%mod;
			if(j>=m) g[i][j]=(g[i][j]+f[i][j])%mod;
		}
		for(int j=k-1;j;--j) suf[i][j]=(suf[i][j+1]+g[i][j])%mod;
	}
	int qwq=1;
	for(int i=1;i<=m;++i) qwq=1ll*qwq*inv[k-i+1]%mod;
	return 1ll*suf[n][1]*qwq%mod;
}
inline int calc2()
{
	int l=0,r=0;
	while(!vis1[a[l+1]]){++l;vis1[a[l]]=1;}
	while(!vis2[a[m-r]]){vis2[a[m-r]]=1,++r;}
	if(l>=k||r>=k) return 0;

	bool ok=0;
	memset(cnt,0,sizeof(cnt));
	for(int i=1;i+k-1<=m;++i)
	{
		bool qwq=0;
		for(int j=i;j<=i+k-1;++j) ++cnt[a[j]];
		for(int j=i;j<=i+k-1;++j)
			if(cnt[a[j]]>1) qwq=1;
		for(int j=i;j<=i+k-1;++j) --cnt[a[j]];
		ok|=(qwq==0);
	}
	if(ok) return 0;

	f[0][l]=suf1[0][l]=1;
	g[0][r]=suf2[0][r]=1;
	for(int j=k-1;j;--j)
	{
		suf1[0][j]=(suf1[0][j+1]+suf1[0][j])%mod;
		suf2[0][j]=(suf2[0][j+1]+suf2[0][j])%mod;
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=k-1;++j)
		{
			f[i][j]=(f[i][j]+1ll*(k-j+1)*f[i-1][j-1]%mod)%mod;
			f[i][j]=(f[i][j]+suf1[i-1][j])%mod;
			g[i][j]=(g[i][j]+1ll*(k-j+1)*g[i-1][j-1]%mod)%mod;
			g[i][j]=(g[i][j]+suf2[i-1][j])%mod;
		}
		for(int j=k-1;j;--j)
		{
			suf1[i][j]=(suf1[i][j+1]+f[i][j])%mod;
			suf2[i][j]=(suf2[i][j+1]+g[i][j])%mod;
		}
	}
	int qwq=0;
	for(int i=1;i+m-1<=n;++i) qwq=(qwq+1ll*suf1[i-1][1]*suf2[n-m-i+1][1]%mod)%mod;
	return qwq;
}
// f[i][j] 填了前 i 个数,最后 j 个数不重复 的方案数
// g[i][j] 填了前 i 个数,最后 j 个数不重复,长度为 m
int main()
{
	n=read();
	k=read();
	m=read();
	for(int i=1;i<=m;++i) a[i]=read();
	inv[0]=inv[1]=1;
	for(int i=2;i<=k;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	int res=1ll*ksm(k,n-m)*(n-m+1)%mod;
	if(check()) printf("%d",(res-calc1()+mod)%mod);
	else printf("%d",(res-calc2()+mod)%mod);
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值