[置顶] 大水题
【问题描述】
dzy 定义一个n^2 位的数的生成矩阵A 为一个大小为n*n 且Aij 为这个数的第i*n+j-n位的矩阵。
现在dzy 有一个数n^2 位的数k,他想知道所有小于等于k 的数的n*n 生成矩阵有多少种。(如果不足n^2 位则补前缀零)
【输入】
第一行一个数n,第二行一个n^2 位的数k
【输出】
仅一行表示答案,答案可能很大,你只需输出答案对10^9 + 7 取模后的结果。
【输入输出样例】
water.in
2
1000
water.out
954
【数据规模和约定】
对于30% 的数据n<=2
对于100% 的数据n <=1000,且n为偶数
【提示】
如果两个生成矩阵在其中一个旋转180 度后可以重叠,则称这两个矩阵是相同的。
【题解】
数位DP
算法1:
直接暴力到底,复杂度是,期望得分30分。
算法2:
令 f(i,n) 表示把i在 n2位十进制下反转得到的数,则有:
分子中的两项都可以用数位DP求出,首先考虑第一项:
令表示DP的状态,表示 k 前 i 位组成的数,表示 k 后 i 位组成的数,表示k 前 i 位倒序组成的数,则定义:
显然这是可以递推的。
接下来考虑第二项:
求出这两项后代入上式即可得到答案,时间复杂度 O(n2 ),期望得分100 分。
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define fp(i,a,b) for(int i=a;i<=b;i++)
#define fq(i,a,b) for(int i=a;i>=b;i--)
#define il inline
#define ll long long
using namespace std;
const int mod=1e9+7;
const int M=500000004;
const int maxn=1010*1010;
ll n,m,ans,cnt,a[maxn],f[maxn][2][2];
char s[maxn];
il int gi()
{
int x=0;
short int t=1;
char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
il void init()
{
n=gi();
n*=n;
scanf("%s",s+1);
fp(i,1,n)
a[i]=s[i]-'0',ans=(ans*10+a[i])%mod;
}
il void work()
{
f[0][0][1]=1;
fp(i,0,n-1)
fp(j,0,1)
fp(k,0,1)
{
if(!f[i][j][k]) continue;
fp(l,0,9)
{
if(j||a[i+1]>=l)
{
bool b=(a[i+1]>l||j)?1:0;
bool c=(a[n-i]>l||l==a[n-i]&&k)?1:0;
(f[i+1][b][c]+=f[i][j][k])%=mod;
}
}
}
cnt=(f[n][0][1]+f[n][1][1])%mod;
cnt=(cnt-f[n/2][1][1]-f[n/2][1][0]-f[n/2][0][1])%mod;
cnt=cnt*M%mod;
ans=((ans-cnt)%mod+mod)%mod;
printf("%lld\n",ans);
}
int main()
{
freopen("water.in","r",stdin);
freopen("water.out","w",stdout);
init();
work();
fclose(stdin);
fclose(stdout);
return 0;
}