Mutual Training for Wannafly Union #6 [SPOJ - VECTAR1] Matrices with XOR property 二维线段树 数位dp

D - Matrices with XOR property

  SPOJ - VECTAR1

Imagine A is a NxM matrix with two basic properties


1) Each element in the matrix is distinct and lies in the range of 1<=A[i][j]<=(N*M)

2) For any two cells of the matrix, (i1,j1) and (i2,j2), if (i1^j1) > (i2^j2) then A[i1][j1] > A[i2][j2] ,where 

1 ≤ i1,i2 ≤ N

1 ≤ j1,j2 ≤ M.

^ is Bitwise XOR


Given N and M , you have to calculatethe total number of matrices of size N x M which have both the properties

mentioned above.  


Input format:

First line contains T, the number of test cases. 2*T lines follow with N on the first line and M on the second, representing the number of rows and columns respectively.


Output format:

Output the total number of such matrices of size N x M. Since, this answer can be large, output it modulo 10^9+7


Constraints:

1 ≤ N,M,T ≤ 1000


SAMPLE INPUT 

1

2

2

SAMPLE OUTPUT 

4

Explanation

The four possible matrices are:

[1 3] | [2 3] | [1 4] | [2 4]

[4 2] | [4 1] | [3 2] | [3 1]

题意:
给你一个n*m的矩阵,要求我们在每个格子上填数字,填数字规则为:
1、每个格子的数字必需不一样,且所填数字必需在1~n*m之间
2、任意两个个格子A[ i1 ][ j1 ]、A[ i2 ][ j2 ],若i1^j1>i2^j2,那么A[ i1 ][ j1 ]>A[ i2 ][ j2 ]
问有多少种解决方案。

一开始打了个10*10的i^j表,除了发现一些对称性质外并没有什么特别的发现。在想是不是有什么规律呢。。。首先肯定大的数字是往大的异或值里填,而异或值相同的格子可以来个全排列。于是用最笨的方法做了超时。。就不干了。。。
接下来题解q巨他们说用数位dp来做,有个热心观众冒出来说用二维线段树离线处理。。。呃呃呃呃啊为啥比赛时我就没有继续尝试呢。。可惜。。

统计在n*m中有多少个i^j = k(其中k的取值是0~1023,1000内的异或值)。那么如何上线段树呢?
首先做离线处理,讲每一个k值在最大数据范围1000的矩阵中标记出现的位置,同时将输入的的询问记录下来。接下来枚举k值,对于每一个k值更新该值的树状数组中的值,然后对每一个输入的询问记录下该k值在n*m的矩阵中有多少个。然后清空树状数组进入下一个k值的记录。
这样就离线地将所有答案都处理好了,输出的时候只要对于每个询问将所有k值的个数全排列后相乘即可,注意开longlong
还是要感谢群里大佬解答啊
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
#include <map>
using namespace std;
const int maxn = 1030;
const int mod = 1e9+7;
int save[maxn][maxn];
long long A[maxn];
int t;
map<int,int> judge;
vector<pair<int,int> > rigi[maxn];
struct unit{
    pair<int,int> ask;
    long long res[maxn];
}in[maxn];
int lowbit(int x) {
    return x & (-x);
}
void update(int x,int y, int val) {
    for(int i = x; i <= maxn; i += lowbit(i)) {
        for(int j = y; j <= maxn; j += lowbit(j)) {
            save[i][j] += val;
        }
    }
}
int query(int x, int y) {
    int sum = 0;
    for(int i = x; i > 0; i -= lowbit(i)) {
        for(int j = y; j > 0; j -= lowbit(j)) {
            sum += save[i][j];
        }
    }
    return sum;
}
void init(){
    int i,j,n,m;
    for(i=1;i<=1000;i++){
        for(j=1;j<=1000;j++){
            rigi[i^j].push_back(make_pair(i, j));
        }
    }
    memset(save, 0, sizeof(save));
    for(i=1;i<=t;i++){
        scanf("%d%d",&n,&m);
        in[i].ask = make_pair(n, m);
        for(j=0;j<=1023;j++){
            in[i].res[j] = 0;
        }
    }
    for(i=0;i<=1023;i++){
        for(j=0;j<rigi[i].size();j++){
            update(rigi[i][j].first, rigi[i][j].second, 1);
        }
        for(j=1;j<=t;j++){
            in[j].res[i] = query(in[j].ask.first, in[j].ask.second);
        }
        for(j=0;j<rigi[i].size();j++){
            update(rigi[i][j].first, rigi[i][j].second, -1);
        }
    }
    A[0] = 1;
    A[1] = 1;
    for(i=2;i<=1023;i++){
        A[i] = (A[i-1]*i)%mod;
    }
}
int main(){
    int i,j;
    scanf("%d",&t);
    long long res;
    init();
    for(i=1;i<=t;i++){
        res = 1;
        for(j=0;j<=1023;j++){
            res = (res*A[in[i].res[j]])%mod;
        }
        printf("%lld\n",res);
    }
    return 0;
}












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值