zoukankan      html  css  js  c++  java
  • CF1336E Chiori and Doll Picking 【线性代数,组合计数】

    题目描述:给定 (n) 个数 (a_iin[0,2^m)),对所有 (k=0,1,dots,m),求 (sum_{Sin {a_i}}[ ext{popcount}(igoplus_{xin S}x)=k])

    数据范围:(nle 2cdot 10^5,mle 53)。部分分 (mle 35)

    本文参考官方题解


    (A) 是给出的数得到的线性基,( ext{span}(A)) 表示 (A) 张成的线性空间, (F(S)=sum_{xin ext{span}(S)}z^x)(P(S)=sum_{xin ext{span}(S)}z^{ ext{popcount}(x)})

    (langle i,j angle= ext{popcount}(i ext{ and } j))。生成函数乘法默认 xor 卷积。

    (langle i,k angle+langle j,k angleequiv langle ioplus j,k angle ( ext{mod} 2)),即乘法(and)关于加法(xor)的分配律。


    首先大家肯定都会 (O(2^{ ext{rank}(A)})),即枚举线性基的子集。

    对于 (mle 35),直接用 meet-in-the-middle,把线性基分成高位和低位两部分,其中低位部分只有在最低 $ lceilfrac{m}{2} ceil$ 位有值。设 (f_{i,S}) 表示对于高位的线性基异或出来的所有值,较高 (lfloorfrac{m}{2} floor) 位的 popcount 为 (i),较低 (lceilfrac{m}{2} ceil)(S) 的方案数。(g_S) 表示低位的线性基异或出 (S) 的方案数。将 (m)(f_i)(g) 做 xor 卷积合并即可。复杂度 (O(m2^{lceilfrac{m}{2} ceil}+m^22^{lfloorfrac{m}{2} floor}))

    结果这个做法对满分做法没有一点用处(

    满分做法使用了根号分治的思想,对于 ( ext{rank}(A)le lfloorfrac{m}{2} floor) 时使用暴力,对于其他情况考虑寻找一个 (O(2^{m- ext{rank}(A)})) 的做法。


    有线性基的一些性质得到:

    第一行:线性空间 做 线性空间内的线性变换 得到它自己。
    第二行:乘法分配律。
    第三、四行:做 FWT。根据这个柿子就可以知道,$	ext{FWT}(F(A))$ 的每一位是 $0$ 或 $2^{	ext{rank}(A)}$。
    第五、六行:如果存在一个 $x$ 使得 $2
    ot|langle k,x
    angle$,则该位不可能为 $2^{	ext{rank}(A)}$,只能为 $0$。
    

    [egin{align*}(z^x) imes F(A)&=F(A) & xin ext{span}(S) \F(A) imes F(A)&=F(A)cdot 2^{ ext{rank}(A)} \ ext{FWT}(F(A))cdot ext{FWT}(F(A))&= ext{FWT}(F(A))cdot 2^{ ext{rank(A)}} \ [z^k] ext{FWT}(F(A))&=sum_{xin ext{span}(A)}(-1)^{langle k,x angle}\&=2^{ ext{rank}(A)}[forall xin ext{span}(A),2|langle k,x angle] \&=2^{ ext{rank}(A)}[forall xin A,2|langle k,x angle]end{align*} ]


    定义 (A) 的正交线性基为 (B),使得 (forall xin A,yin B)(2|langle x,y angle)。并且 ( ext{rank}(A)+ ext{rank}(B)=m)

    (F(B)cdot 2^{ ext{rank}(A)}= ext{FWT}(F(A)))

    至于它怎么求,你可以把线性基中的列进行一些交换,使得它的左边是一个边长 ( ext{rank}(A)) 的单位矩阵。

    然后将右边一个矩形对称到它的左下方,右下方是一个边长为 (m- ext{rank}(A)) 的单位矩阵。

    (放一个官方题解的图)

    很容易检验它就是 (A) 的正交线性基,并且两两之间的 (langle x,y anglein{0,2})


    求出正交线性基之后呢,考虑算答案。设 (G_c=sum_{xge 0}z^x[ ext{popcount}(x)=c])

    [egin{align*} [z^c]P(A)&=[z^0](A imes G_c) \ &=[z^0] ext{IFWT}( ext{FWT}(F(A))cdot ext{FWT}(G_c)) end{align*} ]

    [[z^0] ext{IFWT}(X)=2^{-m}sum_{ige 0}[z^i]X ]

    我们知道 ( ext{FWT}(F(A))=F(B)cdot 2^{ ext{rank}(A)})

    [[z^c]P(A)=2^{ ext{rank}(A)-m}sum_{dge 0}[z^d]P(B)sum_{ige 0}(-1)^iinom{d}{i}inom{m-d}{c-i} ]

    (d) 为枚举 (F(B)) 中每一项的大小,(i) 是 FWT 时的两个集合的交集大小)

    直接计算,时间复杂度 (O(2^{lfloorfrac{m}{2} floor}+mn+m^3))

    总而言之,正交线性基是一个与原线性基“互补”的东西,同时反映了原线性基的性质。也就找到了 (O(2^{m- ext{rank}(A)})) 的做法。

    #include<bits/stdc++.h>
    #define Rint register int
    using namespace std;
    typedef long long LL;
    const int N = 63, mod = 998244353;
    template<typename T>
    inline void read(T &x){
        int ch = getchar(); x = 0;
        for(;ch < '0' || ch > '9';ch = getchar());
        for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    }
    template<typename T>
    inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
    template<typename T>
    inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
    inline int ksm(int a, int b){
        int res = 1; if(b < 0) b += mod - 1;
        while(b){
            if(b & 1) res = (LL) res * a % mod;
            a = (LL) a * a % mod; b >>= 1;
        }
        return res;
    }
    inline void qmo(int &x){x += (x >> 31) & mod;}
    LL lb[N], a[N], b[N], p[N];
    int n, m, k, C[N][N];
    inline void insert(LL x){
        for(Rint i = m - 1;~i;-- i) if((x >> i) & 1){
            if(!lb[i]){lb[i] = x; return;}
            x ^= lb[i];
        }
    }
    inline void dfs(int dep, LL x){
        if(dep == k){++ p[__builtin_popcountll(x)]; return;}
        dfs(dep + 1, x); dfs(dep + 1, x ^ a[dep]);
    }
    int main(){
        read(n); read(m);
        for(Rint i = 1;i <= n;++ i){
            LL x; read(x); insert(x);
        }
        for(Rint i = 0;i < m;++ i)
            if(lb[i]){
                for(Rint j = i - 1;~j;-- j)
                    if((lb[i] >> j) & 1) lb[i] ^= lb[j];
            }
        for(Rint i = 0;i < m;++ i)
            if(lb[i]){
                for(Rint j = 0;j < m;++ j)
                    if(!lb[j]) a[k] = (a[k] << 1) | (lb[i] >> j & 1);
                a[k] |= (1ll << m - 1 - k); ++ k;
            }
        if(k <= (m >> 1)){
            dfs(0, 0); int tmp = ksm(2, n - k);
            for(Rint i = 0;i <= m;++ i) printf("%lld ", p[i] * tmp % mod);
        } else {
            for(Rint i = 0;i < m - k;++ i){
                for(Rint j = 0;j < k;++ j)
                    if((a[j] >> i) & 1)
                        b[i] |= (1ll << j);
                b[i] |= (1ll << m - 1 - i);
            }
            swap(a, b); k = m - k;
            dfs(0, 0); int tmp = ksm(2, n - m);
            C[0][0] = 1;
            for(Rint i = 1;i <= m;++ i){
                C[i][0] = 1;
                for(Rint j = 1;j <= i;++ j)
                    qmo(C[i][j] = C[i - 1][j] + C[i - 1][j - 1] - mod);
            }
            for(Rint c = 0;c <= m;++ c){
                int ans = 0;
                for(Rint d = 0;d <= m;++ d){
                    int tt = 0;
                    for(Rint i = 0;i <= c;++ i){
                        int ttt = (LL) C[d][i] * C[m - d][c - i] % mod;
                        if(i & 1) qmo(tt -= ttt);
                        else qmo(tt += ttt - mod);
                    }
                    qmo(ans += (LL) tt * p[d] % mod - mod);
                }
                printf("%lld ", (LL) ans * tmp % mod);
            }
        }
    }
    
  • 相关阅读:
    关于切图的一道题目
    spm3 基本
    简易路由操作
    IOS 技术层概览
    IOS -C基础
    prototype演变
    css3选择器总结
    Python核心编程(第二版) 第五章习题答案
    Python核心编程(第二版) 第四章习题答案
    Python核心编程(第二版) 第三章习题答案
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/12918990.html
Copyright © 2011-2022 走看看