[ARC100F]Colorful Sequences

题目

题目背景
这是小游戏第几次 A K AK AK 了呢?说也说不清了。想要数的话,就要回到电脑诞生之初,细数其 A K AK AK 之数;就算上厕所也必须仔细盯梢,一不留神,他就已 A K AK AK 了。是以世传之数多有出入,或曰一万万,或曰三万万;后来便成了一个永远不可能为人类所知的数字。

直到五万年后,约莫银河纪元 2000 2000 2000 年时,其信众呼唤普林斯普的神威,于是取出上古圣器——彩虹之裂纹。他们据此推断小游戏 A K AK AK 之数,并以此作为通往力量的钥匙……

题目描述
非普林斯普信众,可读给不可通晓者的糟粕

彩虹之裂纹,共有 k k k 种颜色。这 k k k 远比 7 7 7 要多,因为这彩虹的颜色,并非仅凡胎肉眼可见的 7 7 7 色。他们要用 n n n 个彩虹之裂纹,排成一列,使得存在连续的 k k k 个彩虹之裂纹,颜色互不相同。

此外,有一个长度为 m m m 的彩虹之裂纹的序列。它作为连续子序列,出现在上面排出的长度为 n n n 的序列中 1 1 1 次,就可以认为小游戏 A K AK AK 之数多了 1 1 1 次。注意,一个序列可以出现多次该序列,且出现位置有部分重叠;正如小游戏可以同时 A K AK AK 多场。

那么,我们只需要知道所有可行的序列中,这个 m m m 长度的序列的出现总次数即可。

数据范围与提示
m ⩽ n ⩽ 2.5 × 1 0 4 m\leqslant n\leqslant 2.5\times 10^4 mn2.5×104 k ⩽ 400 k\leqslant 400 k400

思路

我好不容易快要想到正解一次,你却让我输的,这么彻底——哈哈哈哈哈——火卓!

从简单想起。称呼需要计算出现次数的序列是 a a a 。假设我们只算 a a a 出现次数,怎么算?枚举 a a a 的位置就行。假设我们只看 [ 1 , k ] [1,k] [1,k] 是否出现怎么算?用 d p ( i , j ) dp(i,j) dp(i,j) 表示长度为 i i i 的序列,后缀有 j j j 个不同数字,很容易转移,因为 各个数字是等价的

所以把二者缝合一下:枚举 a a a 的位置之后,用 d p \tt dp dp 计算方案。于是我画了张图,很清楚地看到,完整的 “彩虹” 要么在左,要么在内,要么在右。所以分别求一下 a a a 的前缀最长不同数字串、后缀最长不同数字串,然后往两边 d p \tt dp dp 下去就行。时间复杂度 O ( n k ) \mathcal O(nk) O(nk)

可惜我犯了个致命的错误。不过现在说这些也真是可笑;究其根本,还是我太菜。而标杆只是静静地自己 A K AK AK,把别人不会的题都做对,然后不忘说一句:“承认自己强,很难吗?”

今天,我, O n e I n D a r k \sf OneInDark OneInDark,折戟于标杆之剑下,身死魂灭,为天下笑。虽曰人事,岂非天命哉!

这错误是什么呢?不要立刻相信画图,否则你就会知道 “所有三角形都是等腰” 了。我默认了 m > k m>k m>k,没想过,有可能 a a a 整个被包含在 “彩虹” 内。这时候,两边会互相影响,所以原来的分别 d p \tt dp dp 之法行不通了。

那么该怎么办呢?前面说过,各个数字是等价的;所以,如果有一个长度为 m m m 的子串,只知道其各个数字不相同时,共有 x x x 种情况;那么它就是 a a a 的数量则为 ( k − m ) ! k ! ⋅ x \frac{(k-m)!}{k!}\cdot x k!(km)!x 。这系数就是排列数 A k m A_k^m Akm 的倒数。

于是用 d p \tt dp dp 统计所有的长度为 m m m 的、数字互不相同的子串数量。最后除以那个系数就好了。时间复杂度仍然 O ( n k ) \mathcal O(nk) O(nk)

如果能想到这个,很是厉害啊;我能隐约感觉到的 m o t i v a t i o n \rm motivation motivation 就是 a a a 的加入过程和 d p \tt dp dp 的过程很吻合,但终究是什么也没想出来。

代码

可以看出 whole 的部分是我后来拼接上去的。当然,这里就懒得用 d p \tt dp dp 数组中 0 / 1 0/1 0/1 记录是否有 “彩虹” 出现过了;直接减去没出现的情况就行。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
    
    
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MOD = 1e9+7, MAXN = 25001, MAXK = 401;
inline llong qkpow(llong b,int q){
    
    
	llong a = 1;
	for(; q; q>>=1,b=b*b%MOD)
		if(q&1) a = a*b%MOD;
	return a; // [0,MOD)
}

int a[MAXN], n, k, m;
void getDP(int dp[MAXN][MAXK]){
    
    
	for(int i=1,s=0; i<=n; ++i,s=0){
    
    
		dp[i][k] = int((llong(k)*dp[i-1][k]+dp[i-1][k-1])%MOD);
		drep(j,k-1,1){
    
     // normal transfer
			s = (s+dp[i-1][j])%MOD; // suffix sum
			dp[i][j] = (s+llong(k-j+1)*dp[i-1][j-1])%MOD;
		}
	}
}

int zxy[MAXN][MAXK]; // f**k!!!
void eat_shit(int dp[MAXN][MAXK]){
    
    
	for(int i=1,s=0,s2=0; i<=n; ++i,s=0,s2=0){
    
    
		drep(j,k-1,1){
    
     // normal transfer
			s = (s+dp[i-1][j])%MOD; // suffix sum
			s2 = (s2+zxy[i-1][j])%MOD; // not end here
			dp[i][j] = (s+llong(k-j+1)*dp[i-1][j-1])%MOD;
			zxy[i][j] = (s2+llong(k-j+1)*zxy[i-1][j-1])%MOD;
			if(j >= m) zxy[i][j] = (zxy[i][j]+dp[i][j])%MOD;
		}
	}
}

int lv[MAXN][MAXK], rv[MAXN][MAXK];
bool vis[MAXK]; int lst[MAXK];
int main(){
    
    
	n = readint(), k = readint(), m = readint();
	bool self = false; ///< if itself "colorful"
	bool whole = true; ///< if no one appears twice
	for(int i=1,l=1; i<=m; ++i){
    
    
		a[i] = readint();
		l = max(l,lst[a[i]]+1);
		if(lst[a[i]]) whole = false;
		lst[a[i]] = i; // position
		if(i-l+1 == k) self = true;
	}
	int ans = 0;
	if(whole){
    
     // worst case!!!
		lv[0][0] = 1, eat_shit(lv);
		for(int i=1; i!=k; ++i) // invalid
			ans = (ans+zxy[n][i])%MOD;
		for(int i=k; i!=k-m; --i) // fuck it
			ans = int(ans*qkpow(i,MOD-2)%MOD);
		ans = int((qkpow(k,n-m)*(n-m+1)+MOD-ans)%MOD);
		printf("%d\n",ans); return 0;
	}
	
	int len = 1; // match length
	for(; len<=m&&!vis[a[len]]; ++len)
		vis[a[len]] = true; // put tag
	if(self) len = k+1; // assume it's on the left
	lv[0][len-1] = 1; getDP(lv); // for L
	
	memset(vis+1,false,k); // clear
	for(len=0; len!=m&&!vis[a[m-len]]; )
		vis[a[m-len]] = true, ++ len;
	rv[0][len] = 1; getDP(rv); // for R

	for(int l=0; l+m<=n; ++l){
    
    
		ans = int((ans+lv[l][k]*qkpow(k,n-l-m))%MOD);
		llong bad = (qkpow(k,l)+MOD-lv[l][k])%MOD;
		ans = int((ans+bad*rv[n-l-m][k])%MOD);
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/122441480
今日推荐