G - Painting Square CodeForces - 300D(DP,优化)

Vasily the bear has got a large square white table of n rows and n columns. The table has got a black border around this table.

The example of the initial table at n = 5.
Vasily the bear wants to paint his square table in exactly k moves. Each move is sequence of actions:

The bear chooses some square inside his table. At that the square must have a black border painted around it. Also, the square shouldn’t contain a black cell. The number of cells in the square shouldn’t be less than 2.
The bear chooses some row and some column inside the chosen square. Then he paints each cell of this row and this column inside the chosen square. After that the rectangles, formed by the square’s border and the newly painted cells, must be squares of a non-zero area.

An example of correct painting at n = 7 и k = 2.
The bear already knows numbers n and k. Help him — find the number of ways to paint the square in exactly k moves. Two ways to paint are called distinct if the resulting tables will differ in at least one cell. As the answer can be rather large, print the remainder after dividing it by 7340033.

Input
The first line contains integer q (1 ≤ q ≤ 105) — the number of test data.

Each of the following q lines contains two integers n and k (1 ≤ n ≤ 109, 0 ≤ k ≤ 1000) — the size of the initial table and the number of moves for the corresponding test.

Output
For each test from the input print the answer to the problem modulo 7340033. Print the answers to the tests in the order in which the tests are given in the input.

Examples
Input
8
1 0
1 1
3 0
3 1
2 0
2 1
3 2
7 2
Output
1
0
1
1
1
0
0
4

题意:
每次选一个周围都是黑色的正方形,将中间一列一行染色为黑色,分成4个小正方形。
求多少种分法。

思路:
对于n*n的正方形,分一次得到四个 ( ( n 1 ) / 2 ( n 1 ) / 2 ) ((n-1)/2*(n-1)/2) 的小正方形,可以想到当n为偶数的时候无解。所以可以一直将n除2知道n为偶数或者为1,判断最多可以分几层。

定义 a n s [ i ] [ j ] ans[i][j] 为可以分 i i 层的正方形,分 j j 次的方案数,每次划分为4个小正方形,则有状态转移方程:
a n s [ i ] [ j ] = a n s [ i 1 ] [ k 1 ] a n s [ i 1 ] [ k 2 ] a n s [ i 1 ] [ k 3 ] a n s [ i 1 ] [ k 4 ] k 1 + k 2 + k 3 + k 4 = j 1 ans[i][j]=∑ans[i-1][k1]*ans[i-1][k2]*ans[i-1][k3]*ans[i-1][k4],k1+k2+k3+k4=j-1

其中 0 i 30 0 j 1000 0≤i≤30,0≤j≤1000 。因为询问很多,可以想到把ans数组预处理出来,但是按照上面的转移方程复杂度可以达到 30 100 0 4 30*1000^4 ,所以必须得优化。

考虑 n u m [ i ] [ j ] = a n s [ i 1 ] [ k ] a n s [ i 1 ] [ j k ] num[i][j]=∑ans[i-1][k]*ans[i-1][j-k] n u m [ i ] [ j ] num[i][j] 代表分 i i 层的正方形,分 j j 次的方案数,但是这里只能划分为两个小正方形。

通过这样定义,实际上优化了 a n s ans 数组的转移,成了:
a n s [ i ] [ j ] = n u m [ i ] [ k ] n u m [ i ] [ j k 1 ] ans[i][j]=∑num[i][k]*num[i][j-k-1] ,复杂度就成了 30 100 0 2 30*1000^2

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long ll;
const int maxn = 1007;
const int mod = 7340033;

ll ans[31][maxn],num[maxn];

void init() {
    ans[0][0] = 1;
    num[0] = 1;
    for(int i = 1;i <= 30;i++) {
        ans[i][0] = 1;
        for(int j = 1;j <= 1000;j++) {
            for(int k = 0;k < j;k++) { //k和j-1-k
                ans[i][j] = (ans[i][j] + num[k] * num[j - 1 - k] % mod) % mod;
            }
        }
        
        num[0] = 1;
        for(int j = 1;j <= 1000;j++) num[j] = 0;
        for(int j = 1;j <= 1000;j++) {
            for(int k = 0;k <= j;k++) {
                num[j] = (num[j] + ans[i][k] * ans[i][j - k] % mod) % mod;
            }
        }
    }
}

int main() {
    init();
    int T;scanf("%d",&T);
    while(T--) {
        int n,k;scanf("%d%d",&n,&k);
        int cnt = 0;
        while((n % 2) == 1 && n > 1) {
            cnt++;
            n /= 2;
        }
        printf("%lld\n",ans[cnt][k]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/tomjobs/article/details/107868752