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

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
#include
#include
#include
#include
#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);
}

你可能感兴趣的:(noip,DP,数位DP)