递归 [组合数相关]

递 归 递归

递归 [组合数相关]_第1张图片


正 解 部 分 \color{red}{正解部分}

题 目 的 几 何 意 义 : 题目的几何意义: :
给出矩阵最上面一行和最左边一行, ( i , j ) (i,j) (i,j)点的值为 F [ i , j ] F[i, j] F[i,j], F [ i , j ] = F [ i − 1 , j ] ⨁ F [ i , j − 1 ] F[i, j]=F[i-1, j] \bigoplus F[i, j-1] F[i,j]=F[i1,j]F[i,j1], 要求计算矩阵内的元素 .

假设现在要求点 a : a: a: ( x , y ) (x, y) (x,y) 的答案, 上边界上的点 b : b: b: ( i , j ) (i, j) (i,j) a a a 的贡献为

C 总 步 数 垂 直 距 离 & 1 ? F [ x , y ] : 0 C_{总步数}^{垂直距离}\&1?F[x,y]:0 C&1?F[x,y]:0

  • 于是可以先利用已有条件求出 询问矩阵 [ 2 k + 1 → 2 k + 100 , 2 k + 1 → 2 k + 100 ] [2^k+1 \rightarrow 2^k+100, 2^k+1 \rightarrow 2^k+100] [2k+12k+100,2k+12k+100] 的 上, 下边界 ,
  • 然后利用 100 ∗ 100 100*100 100100询问矩阵 上, 左边界预处理出整个矩阵, O ( 1 ) O(1) O(1) 处理询问即可 .

最坏时间复杂度 O ( 2 17 ∗ 200 + 1 0 4 ) O(2^{17}*200+10^4) O(217200+104) .

注 意 上 边 界 不 能 往 右 边 走 ! ! ! ! ! ! ! ! . 所 以 对 上 边 界 , 需 要 先 向 下 走 一 步 , 再 计 算 贡 献 , 左 边 界 同 理 注意上边界不能往右边走!!!!!!!!. 所以对上边界, 需要先向下走一步, 再计算贡献, 左边界同理 !!!!!!!!.,,,


实 现 部 分 \color{red}{实现部分}

如 何 得 到 组 合 数 的 奇 偶 : 如何得到组合数的奇偶: :

c n t [ i ] cnt[i] cnt[i] 表示 i ! i! i! 所包含的 2 2 2因子 的个数, 然后阶乘计算组合数时只需看因子 2 2 2的个数是否大于 0 0 0 即可, 具体看代码.
同时还要注意 c n t [ ] cnt[] cnt[] 数组要开 2 2 2 倍 .

#include
#define reg register

const int maxn = (1<<17) + 105;

int K;
int Q;
int top[maxn];
int cnt[maxn<<1];
int left[maxn];
int A[105][105];

int C(int n, int m){ return cnt[n]-cnt[m]-cnt[n-m]<=0; }

int main(){
        freopen("recursion.in", "r", stdin);
        freopen("recursion.out", "w", stdout);
        scanf("%d", &K); K = 1<<K;
        for(reg int i = 2; i <= 100+K; i ++) scanf("%d", &top[i]);
        for(reg int i = 2; i <= 100+K; i ++) scanf("%d", &left[i]);
        for(reg int i = 1; i <= (100+K)*2; i ++){ // --
                int tmp = i, tmp_2 = 0;
                while(!(tmp & 1)) tmp >>= 1, tmp_2 ++;
                cnt[i] = cnt[i-1] + tmp_2;
        }
//        for(reg int i = 1; i <= 100; i ++) printf("%d\n", cnt[i]);
        /*
        for(reg int i = 1; i <= 100; i ++){ // a: (K+1, K+i)
                for(reg int j = 2; j <= K+i; j ++) // b: (1, j)
                        A[1][i] ^= C(K+K+i-j-1, K-1)*top[j];
                for(reg int j = 2; j <= K+i; j ++) // b: (j, 1)
                        A[1][i] ^= C(K+K+i-j-1, K+i-2)*left[j];
        }
        for(reg int i = 1; i <= 100; i ++){ // a:(K+i, K+1)
                for(reg int j = 2; j <= K+i; j ++) // b: (1, j)
                        A[i][1] ^= C(K+i-1+K-j, K+i-2)*top[j];
                for(reg int j = 2; j <= K+i; j ++) // b: (j, 1)
                        A[i][1] ^= C(K+i-j+K-1, K-1)*left[j];
        }
        */
        for(reg int i = 1; i <= 100; i ++){
                int tot = K - 1;
                for(reg int j = K+i; j >= 2; j --)
                        A[1][i] ^= top[j]*C(tot, K-1), tot ++;
                tot = K + i - 1 - 1;
                for(reg int j = K+1; j >= 2; j --)
                        A[1][i] ^= left[j]*C(tot, K+i-2), tot ++;

        }
        for(reg int i = 2; i <= 100; i ++){
                int tot = K - 1;
                for(reg int j = K+i; j >= 2; j --)
                        A[i][1] ^= left[j]*C(tot, K-1), tot ++;
                tot = K + i - 2;
                for(reg int j = K+1; j >= 2; j --)
                        A[i][1] ^= top[j]*C(tot, K+i-2), tot ++;
        }
        for(reg int i = 2; i <= 100; i ++)
                for(reg int j = 2; j <= 100; j ++) A[i][j] = A[i-1][j] ^ A[i][j-1];
        /*
        for(reg int i = 2; i <= 100; i ++){
                for(reg int j = 2; j <= 100; j ++)
                        printf("%d ", A[i][j]);
                printf("\n");
        }
        */
        scanf("%d", &Q);
        while(Q --){
                int n, m;
                scanf("%d%d", &n, &m);
                printf("%d\n", A[n][m]);
        }
        return 0;
}

你可能感兴趣的:(数学-组合数相关,Second)