1. 程式人生 > >大水題(water)

大水題(water)

open int [1] eve i++ mes gre cto 一個

題目描述
dzy 定義一個 $n^2$ 位的數的生成矩陣 $A$ 為一個大小為 $n \times n$ 且 Aij 為這個數的第 $i \times n+j-n$ 位的矩陣。
現在 dzy 有一個數 $n^2$ 位的數 k,他想知道所有小於等於 k 的數的 $n \times n$ 生成矩陣有多少種。(如果不足 $n^2$ 位則補前綴零)
輸入輸出格式
輸入格式
第一行一個數 $n$,第二行一個 $n^2$ 位的數 $k$
輸出格式
僅一行表示答案,答案可能很大,你只需輸出答案對 $10^9 + 7$ 取模後的結果。
樣例 1

輸入1
2
1000
輸出1
954
數據範圍
對於 $30\%$ 的數據 $n \le 2$
對於 $100\%$ 的數據 $n \le 1000$,且 $n$ 為偶數

提示
如果兩個生成矩陣在其中一個旋轉 $\color{green}{180}$ 度後可以重疊,則稱這兩個矩陣是相同的。

題解:

這道題看了就一臉懵逼,大火題被說成了大水題了.........
言歸正傳:
先和網上一樣貼公式:
設$f(i)$為$i$在$n^2$位中翻轉後的數。
$$ans=k- \frac{\sum_{i}^{f(i) \in [1,k]}1 - \sum_{i}^{f(i) \in [1,k]}(f(i)==i)}{2}$$就是翻轉之後的數的個數減去回文數,再除以2就是重復統計的數了。
設$$f[i][j][k],i \in [1,k],j \in [0,1],k \in [0,1]$$為當前枚舉到第i位,前i位是否是嚴格的小於k的前i位的(是為1否為0),將前i位翻轉之後的數是否是嚴格小於等於k的後i位的。那麽答案為:

$$ans=k- \frac{(f[n][1][1]+f[n][0][1])-(f[n/2][1][0]+f[n/2][1][1]+f[n/2][0][1])}{2}$$
我們在枚舉i,j,k和數字1~9時,就保證前i為嚴格小於等於,那麽j=1就是嚴格小於,否則就是等於了。
$$(f[n][1][1]+f[n][0][1])$$是$$\sum_{i}^{f(i) \in [1,k]}1$$。前n位不管是小於還是等於,只要翻轉之後也在1~n之間就行了。
$$(f[n/2][1][0]+f[n/2][1][1]+f[n/2][0][1])$$是$$\sum_{i}^{f(i) \in [1,k]}(f(i)==i)$$前n/2位翻轉之後把後n/2位給補齊了,正好是一個回文數,那麽前n/2位嚴格小於,自然滿足條件,如果只是等於,就要翻轉之後也是小於等於的。

技術分享
 1 #include<queue>
 2 #include<cstdio>
 3 #include<vector>
 4 #include<cstring>
 5 #include<iostream>
 6 #include<algorithm>
 7 #define RG register
 8 using namespace std;
 9 const int mod=1000000007;
10 long long int n,ans,GG,Two=500000004,X,Y,Z;
11 char s[1000099];
12 int a[1000099],dp[1000099][2][2];
13 int main()
14 {
15   freopen("water.in","r",stdin);
16   freopen("water.out","w",stdout);
17   cin>>n;
18   n*=n;
19   scanf("%s",s+1);
20   for(int i=1;i<=n;i++)
21     {
22       a[i]=s[i]-0;
23       GG=GG*10+a[i];
24       GG%=mod;
25     }
26   dp[0][0][1]=1;
27   for(RG int i=0;i<n;i++)
28     for(RG int j=0;j<=1;j++)
29       for(RG int k=0;k<=1;k++)
30     if(dp[i][j][k]!=0)//加速(沒卵用)
31       for(RG int l=0;l<=9;l++)//枚舉i+1位的數字。
32         {
33           if(l<=a[i+1]||j)//這一位要小於等於或者之前已經小於了。
34         {
35           RG bool b=(l<a[i+1])||j;//這裏不能取等號,因為j代表的是嚴格小於才為1。
36           RG bool c=(l==a[n-i]&&k)||(l<a[n-i]);//。。。嚴格的小於等於,因為n-i是從後往前的如果後面的相等,這以為自然可以相等,否則這以為必須嚴格小於才行。
37           dp[i+1][b][c]+=dp[i][j][k];
38           dp[i+1][b][c]%=mod;
39         }
40           else break;
41         }
42   X=(dp[n][0][1]+dp[n][1][1])%mod; 
43   Y=(dp[n/2][1][0]+dp[n/2][0][1]+dp[n/2][1][1])%mod;
44   Z=((X-Y)*Two)%mod;
45   ans=(GG-Z+mod)%mod;
46   cout<<ans<<endl;
47   return 0;
48 }
View Code

大水題(water)