zoukankan      html  css  js  c++  java
  • Codeforces 662C(快速沃尔什变换 FWT)

    感觉快速沃尔什变换和快速傅里叶变换有很大的区别啊orz

    不是很明白为什么位运算也可以叫做卷积(或许不应该叫卷积吧)

    我是看 http://blog.csdn.net/liangzhaoyang1/article/details/52819835 里的快速沃尔什变换

    这里说一下自己的理解吧,快速傅里叶变换是计算卷积的,就是∑f(x)*g(n-x)这种

    快速沃尔什变换也是计算∑f(x)*g(y) ,但这里是计算所有的满足x^y = n(卷积是计算x+y=n)的和

    当然,异或也可以换成&,|这些运算符。

    正是因为这一点不同,所以fwt与fft有不同的构造方式,具体见引用的博客里的内容

    下面说这道题的题解

    题意很简单:就是给出一个01矩阵,每一次可以把一行或一列翻转,不限次数,计算最少有多少个1

    首先,每一行只需被翻一次或者不翻,可以证明翻奇数次和翻一次等价,不翻和翻偶数次等价

    所以就可以先暴力枚举某一行翻没翻,这样有一个m*2^n的复杂度。

    那么怎么考虑用fwt呢

    考虑一个翻的方案S,实际上第i列答案就是 min(f(S^a[i]), n - f(S^a[i])) (f可以计算1的个数)

    所以也就是说,所有的S^a[i]为同一个值的答案是相同的(想一想x^y=n)

    那么就处理出来dp[i] = min(f(i), n - f(i))和原矩阵的列中有多少个是i

    对于一个方案S, 答案就是∑dp[S^i]*num[i] (注意x^y = S)

    所以我们只需要算出dp和num的卷积,然后从中统计答案即可

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    typedef long long LL;
    class FWT{
    public:
        void fwt(LL *a, int n){
            for(int d = 1; d < n; d <<= 1){
                for(int m = d<<1, i = 0; i < n; i += m){
                    for(int j = 0; j < d; j++){
                        LL x = a[i+j], y = a[i+j+d];
                        a[i+j] = x+y; a[i+j+d] = x-y;
                        //and a[i+j] = x+y;
                        //or a[i+j+d] = x+y;
                    }
                }
            }
        }
        void ufwt(LL *a, int n){
            for(int d = 1; d < n; d <<= 1){
                for(int m = d<<1, i = 0; i < n; i += m){
                    for(int j = 0; j < d; j++){
                        LL x = a[i+j], y = a[i+j+d];
                        a[i+j] = (x+y)/2; a[i+j+d] = (x-y)/2;
                        //and a[i+j] = x-y
                        //or a[i+j] = y-x
                    }
                }
            }
        }
        void work(LL *a, LL *b, int n){
            fwt(a, n);
            fwt(b, n);
            for(int i = 0; i < n; i++) a[i] *= b[i];
            ufwt(a, n);
        }
    }myfwt;
    const int maxn = 100086;
    char str[maxn];
    LL dp[(1<<20)+5], num[(1<<20)+5], a[maxn];
    int n, m;
    int calc(int x){
        int ans = 0;
        for(; x; x >>= 1) ans += (x&1);
        return ans;
    }
    int main()
    {
        cin>>n>>m;
        for(int i = 0; i < n; i++){
            cin>>str;
            for(int j = 0; j < m; j++)
                a[j] |= ( (str[j]-'0') << i);
        }
        for(int i = 0; i < m; i++) num[a[i]]++;
        for(int i = 0; i < (1<<n); i++){
            int ans = calc(i);
            dp[i] = min(ans, n-ans);
        }
        myfwt.work(dp, num, 1<<n);
        LL ans = 1e18;
        for(int i = 0; i < (1<<n); i++) ans = min(ans, dp[i]);
        cout<<ans<<endl;
        return 0;
    }
  • 相关阅读:
    数据结构上机思考
    hdu1005,循环节
    网络赛总结
    icpc沈阳网络赛。cake cake!
    树的基础代码
    网络赛第一场
    欧拉函数
    欧拉函数
    多校第十场
    (环上)最大子段和
  • 原文地址:https://www.cnblogs.com/Saurus/p/6910016.html
Copyright © 2011-2022 走看看