2019.09.07【NOIP提高组】模拟 A 组

T1:这题较为简单。

只需求出去掉每一个位置之后逆序对个数减少多少个,然后推一下式子就好了。

T2:这题比较灵活。

我们把所有的字符串放在一个矩阵上,然后从左往右考虑每一列,我们发现其实所有字符串就是在不断地分组。把每一列中字母相同的字符串分到同一组,然后在看下一列。最终m列之后每一个字符串都别分到了不同的组,这样就是合法的。

那么我们就可以dp了。设f[i][l][r][c]表示现在处理到第i列,l~r行最大的字符填c的且当前l~r行的字典序有严格保证的方案数。(注意我们是从m往1处理的)。

在转移时,我们枚举一个r1和d,表示第i列r+1~r1行填d,接着我们就f[i][l][r1][d]+=f[i][l][r][c]*f[i+1][r+1][r1][e]。由于e的任意性,我们可以边求f边求和,那么转移时我们就不用枚举e了。

这样的复杂度时能过的。但是还有一些细节要注意,比如说我们要判断r+1~r1填d是否合法,还有就是在l~r都能填c的情况下,我们要把g[i+1][l][r]作为f[i][l][r][c]的初值(g就是f的和)。

细节较多,贴一下代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#define ll long long
#define MAXN 60
#define MAXM 30
#define MAXV 30
#define mod 990804011

ll a[MAXN][MAXM],f[MAXM][MAXN][MAXN][MAXV],g[MAXM][MAXN][MAXN],n,m,ans;
char a1[MAXN][MAXM];
int main()
{
//freopen("b.in","r",stdin);
//freopen("b.out","w",stdout);
ll i,j,l,r,r1,c,d,p,tf,x;
scanf("%lld\n",&n);
for(i=1;i<=n;i++)scanf("%s\n",a1[i]);
for(i=1;i<=n;i++)
	if(strlen(a1[i])>m)m=strlen(a1[i]);
for(i=1;i<=n;i++)
	for(j=1;j<=m;j++)
		if(a1[i][j-1]!='?')
		{
			if('a'<=a1[i][j-1]&&a1[i][j-1]<='z')a[i][j]=a1[i][j-1]-'a'+1;
		}
		else a[i][j]=-1;
for(l=1;l<=n;l++)
	if(a[l][m]!=-1)f[m][l][l][a[l][m]]=1;
	else
		for(c=1;c<=26;c++)f[m][l][l][c]=1;
for(c=0;c<=26;c++)
	for(l=1;l<=n;l++)
		for(r=l;r<=n;r++)
		{
			for(d=c+1;d<=26;d++)
				if(r+1<=n)
					if(!(a[r+1][m]!=-1&&a[r+1][m]!=d))
						f[m][l][r+1][d]=(f[m][l][r+1][d]+f[m][l][r][c])%mod;
			g[m][l][r]=(g[m][l][r]+f[m][l][r][c])%mod;
		}
for(i=m-1;i>=1;i--)
	for(c=0;c<=26;c++)
		for(l=1;l<=n;l++)
		{
			tf=1;
			for(r=l;r<=n;r++)
			{
				if(a[r][i]!=-1&&a[r][i]!=c||a[r][i]==-1&&c==0)tf=0;
				if(tf==1)f[i][l][r][c]=(f[i][l][r][c]+g[i+1][l][r])%mod;
				for(d=c+1;d<=26;d++)
					for(r1=r+1;r1<=n;r1++)
					{
						if(a[r1][i]!=-1&&a[r1][i]!=d)break;
						f[i][l][r1][d]=(f[i][l][r1][d]+f[i][l][r][c]*g[i+1][r+1][r1])%mod;
					}
				g[i][l][r]=(g[i][l][r]+f[i][l][r][c])%mod;
			}
		}
printf("%lld",g[1][1][n]);
}

T3:这是一道偏数学的题目。

首先我们把所有x[i]减1,然后发现f就等于从(0,0...,0)走到(x[1],x[2],...,x[k])的方案数%2。

然后我们就可以开始推式子:

首先对于二维情况:f=C(x1+x2,x2)。

那么扩展到k维的情况就是:
f=\binom{x1+x2+...+xk}{x1}*\binom{x2+x3...+xk}{x2}*...*\binom{x(k-1)+xk)}{x(k-1))}*\binom{xk}{xk} =\frac{(x1+x2+...+xk)!*(x2+x3+...+xk)!)*...*(xk)!}{(x1)!*(x2)!*...*(xk)!*(x2+x3+...+xk)!*(x3+x4+...+xk)!*...*(xk)!} =\frac{(\sum x[i])!}{\prod (x[i]!)}

现在就是要求f的奇偶性。首先很容易得出n!中2因子的个数=\left \lfloor \frac{x}{2} \right \rfloor+\left \lfloor \frac{x}{4} \right \rfloor+\left \lfloor \frac{x}{8} \right \rfloor+...+\left \lfloor \frac{x}{2^{k}} \right \rfloor,即将x的二进制去掉后k位之后的和。

这时又到感性理解的时候了:如果存在某个x[i]和x[j]相加时在二进制状态下发生了进位,那么f一定含有2因子,即f的贡献为0。

这其实不难理解。那么现在我们可以得出结论,如果想要f对答案有贡献,那么所有x的二进制位上最多只能有一个1(即不能进位)。

那么就可以数位+状压dp了。设f[i][s]表示当前到第i位,其中s状态下的x前面已经顶到了上限(就是数位dp的常见套路)的方案数。枚举所有x的第i-1位填什么,即一个新的01集合s1,判断是否合法后转移即可。

最后要优化一下才能过。

发布了149 篇原创文章 · 获赞 24 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/chiyankuan/article/details/100677195