【NOIP2014八校联考第1场第2试9.21】大水题(water)

来源:互联网 发布:龙江网络宽带客服电话 编辑:程序博客网 时间:2024/05/04 15:36

Description

dzy 定义一个n^2 位的数的生成矩阵A 为一个大小为n*n 且Aij 为这个数的第i*n+j-n位的矩阵。
现在dzy 有一个数n^2 位的数k,他想知道所有小于等于k 的数的n*n 生成矩阵有多少种。(如果不足n^2 位则补前缀零)

Solution

其实题意转化一下就是在k里面找到所有的要求的数的个数(如果翻转过来也存在,那么只算一次)
我们正难则反。
设f(i)为i在n2位中翻转后的数。
ans=kf(i)[1,k]f(i)=i2
上面的式子很显然,就是找出能翻转的数的个数/2(除去回文数)
那么现在就是经典的数位DP的时间了。
f[i][p][q]为,做完i位的时候,是否与k的前i位相等(肯定是≤),翻转过来是否爆掉后i位,所以p和q是0,1状态。
然后求出f之后。
ans=k(f[n][1][1]+f[n][0][1])(f[n/2][1][0]+f[n/2][1][1]+f[n/2][0][1])2

Code

#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=1007*1007,mo=1000000007;typedef long long ll;ll i,j,k,l,t,n,m,ans,x,y,er,dan;ll a[maxn],f[maxn][2][2],b,c;char s[maxn];int main(){    er=500000004;    scanf("%lld",&n);n=n*n;    scanf("%s",s+1);    fo(i,1,n)a[i]=s[i]-'0',ans=(ans*10+a[i])%mo;    f[0][0][1]=1;    fo(i,0,n-1){        fo(j,0,1){            fo(k,0,1){                if(!f[i][j][k])continue;                fo(l,0,9){                    if(j||a[i+1]>=l){                        b=(a[i+1]>l)||j;                        c=(a[n-i]>l)||(l==a[n-i]&&k);                        (f[i+1][b][c]+=f[i][j][k])%=mo;                    }                }            }        }    }    dan=(f[n][0][1]+f[n][1][1])%mo;    dan=(dan-f[n/2][1][1]-f[n/2][1][0]-f[n/2][0][1])%mo;    dan=dan*er%mo;    ans=((ans-dan)%mo+mo)%mo;    printf("%lld\n",ans);}
1 0