HDU 6358 Innocence

题目
n n n个数,每个数在 [ L , R ] [L,R] [L,R]间,异或和为 K K K的方案数。

考虑 N N N个数 a i a_i ai每个数在 [ 0 , R ] [0,R] [0,R]之间的问题。
发现如果枚举一个 i i i表示对于 ∀ j ∈ [ 1 , N ] , k ∈ [ i + 1 , 29 ] \forall j\in [1,N],k\in[i+1,29] j[1,N],k[i+1,29] a j > > k & 1 = R > > k & 1 a_{j}>>k\&1 = R>>k\&1 aj>>k&1=R>>k&1即所有数在 i i i位以上都和上界相等,并且在 i i i位存在一个 a i a_i ai和上界 R R R不相等,那么剩下的 i − 1 i-1 i1位就有了一个可以任意支配的数,我们可以把其他数的后 i − 1 i-1 i1位填了,然后用这个数的后 i − 1 i-1 i1位凑成 K K K的后 i − 1 i-1 i1位。
那么现在就只需要前 [ i , 29 ] [i,29] [i,29]位的异或值分别对位相等即可。
[ i + 1 , 29 ] [i+1,29] [i+1,29]位的异或值是固定的,直接判断即可。
i i i位的异或值就相当于一个简单 d p dp dp,可以用矩阵快速幂优化。
具体是 f i , j , k f_{i,j,k} fi,j,k表示前 i i i个数,异或值为 j j j,并且 k = 1 k=1 k=1则存在一个 a p ∈ [ 1 , i ] a_{p\in[1,i]} ap[1,i]在这一位和上界不相等。
把后两维看作矩阵就可以有理有据的矩阵乘法优化了。
实际上 R R R不一定对于每一个数都必须相等,只是这时就不是矩阵快速幂而是线段树维护矩阵积写动态 d p dp dp了。

拓展到 [ L , R ] [L,R] [L,R]也很简单。
就是每次选上界可以是 R R R也可以是 L − 1 L-1 L1,但是选 L − 1 L-1 L1的时候需要乘上一个 − 1 -1 1的容斥系数。
注意到这时 [ i + 1 , 29 ] [i+1,29] [i+1,29]位的异或值不一定是固定的,但是我们只需要额外记录一下选出 R R R个数的奇偶性就可以判断出选出 L L L个数的奇偶性然后判断 [ i + 1 , 29 ] [i+1,29] [i+1,29]位的异或值了。

所以最后转移矩阵是 8 × 8 8 \times 8 8×8的。
注意复杂度不要带 Q Q Q,稍微预处理一下。

A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define mod 1000000007
using namespace std;

struct mat{
    int a[8][8];
    mat(int d=0){memset(a,0,sizeof a);rep(i,0,7) a[i][i] = d;}
    mat operator *(const mat &B)const{
        mat r;
        rep(i,0,7) rep(j,0,7) rep(k,0,7) 
            r.a[i][k] = (r.a[i][k] + 1ll * a[i][j] * B.a[j][k]) % mod;
        return r;
    }
}A[30];

mat Pow(mat b,int K){
    mat r(1);
    for(;K;K>>=1,b=b*b) if(K&1) r=r*b;
    return r;
}

int n,L,R,Q,K,C[2];

int main(){
    int T;
    for(scanf("%d",&T);T--;){
        scanf("%d%d%d%d",&n,&C[0],&C[1],&Q);
        C[0] --;
        
        
            per(i,29,0) {
                memset(A[i].a,0,sizeof A[i].a);
                rep(a,0,1) rep(b,0,1) rep(c,0,1) rep(d,0,1) if(C[d] >= 0) rep(e,0,C[d]>>i&1){
                    int x = a ^ e , y = b | (e < (C[d] >> i & 1)) , z = c ^ d;
                    if(!b && y)
                        A[i].a[a * 4 + b * 2 + c][x * 4 + y * 2 + z] += (d == 1 ? 1 : -1);
                    else if(e < (C[d] >> i & 1))
                        A[i].a[a * 4 + b * 2 + c][x * 4 + y * 2 + z] += (d == 1 ? 1 : -1) << i;
                    else 
                        A[i].a[a * 4 + b * 2 + c][x * 4 + y * 2 + z] += (d == 1 ? 1 : -1) * ((C[d] & ((1 << i) - 1)) + 1);
                }
                A[i] = Pow(A[i],n);
            }
        
        for(;Q--;){
            scanf("%d",&K);
            int ans = 0;
        	per(i,29,0){
                rep(a,0,1) rep(c,0,1) if(((c * C[1]) ^ ((n - c & 1) * C[0]) ^ K) >> i+1 == 0 && a == (K >> i & 1)){
                    ans = (ans + A[i].a[0][a * 4 + 1 * 2 + c]) % mod;
                }
                if(i == 0){
                	rep(a,0,1) rep(c,0,1) if(((c * C[1]) ^ ((n - c & 1) * C[0]) ^ K) == 0)
                		ans = (ans + A[i].a[0][a * 4 + c]) % mod;
				}
            }
            printf("%d\n",(ans+mod)%mod);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值