Codeforces 662C(快速沃尔什变换 FWT)

本文探讨了快速沃尔什变换(FWT)与快速傅里叶变换(FFT)的区别,并详细解析了一道利用FWT求解矩阵中最小1数量的问题。通过枚举行翻转状态并使用FWT进行优化,实现了高效计算。

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

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

 

我是看 http://blog.youkuaiyun.com/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;
}

 

转载于:https://www.cnblogs.com/Saurus/p/6910016.html

### Codeforces 平台上的快速幂算法题目及相关实现 #### 快速幂算法简介 快速幂是一种高效的计算 $a^b \mod c$ 的算法,其时间复杂度为 $O(\log b)$。通过将指数分解成二进制形式并利用平方倍增的方式减少乘法次数来提高效率。 --- #### 题目解析与实现方法 以下是两道来自 Codeforces 平台上涉及快速幂的经典题目及其解决方案: --- ##### **题目一:Codeforces 1594B** 这是一道典型的快速幂应用题,目标是找到第 $k$ 个以 $n$ 为底的幂底数累加的结果[^1]。 ###### 解决方案 给定整数 $n$ 和 $k$,我们需要按照 $k$ 的二进制表示逐位判断哪些位置对应的幂需要被加入最终结果中。具体实现如下: ```cpp #include <iostream> #define ll long long using namespace std; const int MOD = 1e9 + 7; int main() { int t; cin >> t; while (t--) { ll n, k; cin >> n >> k; ll ans = 0, s = 1; // 初始化答案和当前幂值 while (k != 0) { if (k & 1) { // 如果当前位为1,则加上对应幂值 ans = (ans + s) % MOD; } s = s * n % MOD; // 更新幂值 k >>= 1; // 右移一位 } cout << ans << endl; } return 0; } ``` 此代码的核心在于使用 `while` 循环逐步处理 $k$ 的每一位,并根据是否为 $1$ 来决定是否将其对应的幂值加入到总和中。 --- ##### **题目二:CodeForces - 894B Ralph And His Magic Field** 本题同样涉及到快速幂的应用,但更侧重于组合数学中的计数问题[^2]。 ###### 解决方案 对于输入的三个参数 $n$, $m$, 和 $k$,我们可以通过快速幂函数高效地求解 $(2^{(n-1)})^{(m-1)} \mod INF$ 的值。如果 $k=-1$ 且 $(n+m)\%2\neq0$,则直接返回 $0$;否则继续执行快速幂逻辑。 ```cpp #include <iostream> using namespace std; const long long INF = 1e9 + 7; long long fast_pow(long long base, long long exp) { long long result = 1; base %= INF; while (exp > 0) { if (exp & 1) { result = (result * base) % INF; } base = (base * base) % INF; exp >>= 1; } return result; } int main() { long long n, m, k; cin >> n >> m >> k; if (k == -1 && (n + m) % 2 != 0) { cout << "0" << endl; return 0; } cout << fast_pow(fast_pow(2, n - 1), m - 1) << endl; return 0; } ``` 这段程序定义了一个通用的快速幂辅助函数 `fast_pow`,用于简化主流程中的幂运算部分[^2]。 --- #### 总结 以上两个例子展示了如何在不同场景下灵活运用快速幂技术解决问题。无论是简单的累加还是复杂的嵌套幂运算,都可以借助这一技巧显著提升性能。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值