CodeForces - 361E Levko and Strings

Discription

Levko loves strings of length n, consisting of lowercase English letters, very much. He has one such string s. For each string t of length n, Levko defines its beauty relative to s as the number of pairs of indexes ij (1 ≤ i ≤ j ≤ n), such that substring t[i..j] is lexicographically larger than substring s[i..j].

The boy wondered how many strings t are there, such that their beauty relative to sequals exactly k. Help him, find the remainder after division this number by1000000007 (109 + 7).

A substring s[i..j] of string s = s1s2... sn is string sisi  +  1... sj.

String x  =  x1x2... xp is lexicographically larger than string y  =  y1y2... yp, if there is such number r (r < p), that x1  =  y1,  x2  =  y2,  ... ,  xr  =  yr and xr  +  1 > yr  +  1. The string characters are compared by their ASCII codes.

Input

The first line contains two integers n and k (1 ≤ n ≤ 2000, 0 ≤ k ≤ 2000).

The second line contains a non-empty string s of length n. String s consists only of lowercase English letters.

Output

Print a single number — the answer to the problem modulo 1000000007 (109 + 7).

Examples

扫描二维码关注公众号,回复: 104932 查看本文章
Input
2 2
yz
Output
26
Input
2 3
yx
Output
2
Input
4 7
abcd
Output
21962


不难想到设状态 f[i][j] 表示:已经考虑了第i位往后的字符,并且此时有j对 T 字典序严格大于 S的区间的方案数。
根据设定的状态,我们可以直接得出一个 O(N^3) 的转移:
当我们要计算 f[i][?] 的时候,我们先枚举第二维是多少,然后再枚举T[i~n]与S[i~n]第一个不一样的字符对 T[k]&&S[k] 在哪里,然后算贡献。
也就是 f[i][j] = ∑ (s[k] - 1) * f[k+1][j] + ∑ (26 - s[k]) * f[k+1][j- (n-k+1) * (k-i+1)]. (i<=k<=n)

现在来考虑一下怎么优化这个dp。
第一个∑比较好优化,直接用一个 g[j] 记录 (s[k] - 1) * f[k+1][j] 这个后缀和就好了。
第二个∑看起来好复杂啊。。。。这该咋优化。

考虑我们枚举i的时候,i和n都是固定的,所以 (n-k+1) * (k-i+1) 此时就是一个关于 k 的开口向下的二次函数,发现k靠近i或者靠近n的时候,这个函数值不是很大,而当k趋近于 (n+i)/2 的时候,这个函数值很大,很可能没法转移。
这告诉我们什么?
第二种转移的实际路径其实特别少,并且都是 一个 前缀 + 一个 后缀 (对于k来说)的形式,所以我们只需要当j增加的时候记录 前缀右端点 和 后缀左端点的移动,然后直接暴力转移即可。

那么怎么证明这个函数的转移路径真的很少呢?
我们来列一下式子,实际的转移路径总数 = ∑ ∑ ∑ [(n+2-i-j)*j <= k]
可以抽象成 二次函数 y=x(1-x) ,y=x(2-x) ...,y=x(n+2-x) 在 y=1,y=2....y=k直线 下的整点数量和,可以发现的确是很少的hhhh(谁让我也不会具体证明啦)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2005,ha=1e9+7;
inline void add(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
inline int ADD(int x,int y){ x+=y; return x>=ha?x-ha:x;}
int f[maxn][maxn],n,m,g[maxn],k,ans=0;
char s[maxn];

inline void solve(){
	f[n+1][0]=1;
	for(int i=n;i;i--){
		for(int j=0;j<=k;j++) add(g[j],f[i+1][j]*(ll)(s[i]-'a')%ha);
		
		int L=i-1,R=n+1;
		for(int j=0;j<=k;j++){
			while(j>=(n-L)*(L+2-i)&&L+1<R) L++;
			while(j>=(n-R+2)*(R-i)&&L+1<R) R--;
			for(int l=i;l<=L;l++) add(f[i][j],('z'-s[l])*(ll)f[l+1][j-(n-l+1)*(l-i+1)]%ha);
			for(int l=n;l>=R;l--) add(f[i][j],('z'-s[l])*(ll)f[l+1][j-(n-l+1)*(l-i+1)]%ha);
			add(f[i][j],g[j]);
		}
		
		add(f[i][0],1);
	}
}

int main(){
	scanf("%d%d",&n,&k);
	scanf("%s",s+1);
	solve();
	printf("%d\n",f[1][k]);
	return 0;
}
 
  

  

 
 
 

猜你喜欢

转载自www.cnblogs.com/JYYHH/p/8984831.html