JZOJ6343. 【NOIP2019模拟2019.9.7】Medium Counting

Description

在这里插入图片描述
1n50,1L201\leq n \leq 50,1\leq L \leq 20

Solution

  • 简单的计数,但是我的考虑方向并不正确,设的状态也不好做,导致正解其实并不难,但是我在考场上没有做出来。
  • 既然已经给定了顺序,那么一段满足条件一定是前几个字符完全一样,后面再区分开来。
  • 如果我们按照从后往前的顺序做,就很好转移,有点类似区间DP。
  • f[i][l][r][c]f[i][l][r][c]表示llrr的后i位已经确定并且能区分开来,第i位最大的字符为c
  • 枚举一个rr',让r+1~r’的第i位都为字符c’,转移到f[i][l][r][c]f[i][l][r'][c']
  • 要满足c<c’,所以用一个前缀和优化一下就好了。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 55
#define maxl 22
#define ll long long 
#define mo 990804011
using namespace std;

int n,i,j,k,L,l,r,len[maxn];
char ch;
int s[maxn][maxl];
ll f[maxl][maxn][maxn][27];

int main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		for(ch=getchar();(ch<'a'||ch>'z')&&ch!='?';ch=getchar());
		for(;(ch>='a'&&ch<='z')||ch=='?';ch=getchar()) {
			++len[i];
			if (ch=='?') s[i][len[i]]=-1; else 
				s[i][len[i]]=ch-'a'+1;
		}
		L=max(L,len[i]);
	}
	for(i=1;i<=n;i++) f[L+1][i][i][26]=1;
	for(k=L;k>=1;k--){
		for(l=1;l<=n;l++) if (s[l][k]==0) f[k][l][l][0]=1;
		for(l=1;l<=n;l++) for(r=l;r<=n;r++) {
			for(j=1;j<=26;j++){
				f[k][l][r][j]=f[k][l][r][j-1];
				for(i=r;i>l&&(s[i][k]==-1||s[i][k]==j);i--) 
					f[k][l][r][j]+=f[k][l][i-1][j-1]*f[k+1][i][r][26]%mo;
				if (i==l&&(s[i][k]==-1||s[i][k]==j)) 
					f[k][l][r][j]+=f[k+1][l][r][26];
				f[k][l][r][j]%=mo;
			}
		}
	}
	printf("%lld",f[1][1][n][26]);
}

posted @ 2019-09-07 20:27  Deep_Thinking  阅读(186)  评论(0编辑  收藏  举报