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
#include
#include
#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的奇偶性。首先很容易得出n!中2因子的个数=,即将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,判断是否合法后转移即可。
最后要优化一下才能过。