牛客小白月赛25 J异或和之和 (组合数+位运算)

牛客小白月赛25 J异或和之和

第一篇博客回顾一下上周的牛客小白月赛。
先来看一下题目题目链接
在这里插入图片描述

之前从来没有认识到逆元的重要性,直到被J题卡了快一个小时,赛后问了大佬才意识到自己犯了很严重的错误orz。

先对题目分析一下,如果用一般的方法求出n的所有三元组再进行异或运算,那是必T无疑的,因此我们想到利用位运算的性质,如果三个数连续进行异或,只有两种情况会出1,分别是1 1 1之间的异或和1 0 0 之间的异或,那么我们只需要对每一位进行求组合数算出分别有几种出1的可能,然后依次相加就可以很容易得出结论。最后计算一下时间复杂度,long long 类型最多64位,N * 64 * k的复杂度显然满足要求。
其实这题的组合数比较水,不用逆元也可以过 ,下面来看两种求解的方法吧。

1.不用逆元
直接看代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200010;
const int mod = 1e9 + 7;
int c[64], n;
ll res;
inline ll C1(int x) {
    if (x >= 3) return 1ll * x * (x - 1) * (x - 2) / 6;
    else return 0;
}
inline ll C2(int x) {
    if (x >= 2) return 1ll * x * (x - 1) / 2;
    else return 0;
}
 
int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        ll x; int  b = 0;
        cin >> x;
        while (x) c[b++] += (x & 1), x >>= 1;
    }
    ll bs = 1;
    for (int i = 0; i < 64; i++) {
        res = (res + (1ll * C1(c[i]) % mod + 1ll * c[i] * C2(n - c[i]) % mod) * bs) % mod;
        bs = (bs << 1) % mod;
    }
    cout << res;
}

2.利用逆元进行求解
利用费马小定理
已知a^(p-1) % p = 1 => a * a ^(p-2) % p = 1(p为质数)
因此a^(p-2)就是我们所求的a的逆元,因此可以用快速幂求。
接着我们来看C(n, m) = n!/(m! * (n-m)!)这个式子,我们需要处理每个数的阶乘,因此我们可以用fac[]数组预先处理一下。
然后将除法转换成n!* inv(n!)
设 m! 的逆元是 N ,那么N = m! ^ (p-2)
设(n - m)! 的逆元是 M ,那么M = (n-m)! ^ (p-2)
那么原式可以转换为n! * N * M % p;

下面是ac代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200010;
const ll mod = 1e9 + 7;
ll fac[N];
int c[64];
ll n, m;
ll quick_pow(ll a, ll b) {
	a = a % mod;
	ll res = 1, base = a;
	while (b) {
		if (b & 1) res = res * base % mod;
		base = base * base % mod;
		b >>= 1;
	}
	return res;
}

void get_fac() {
	fac[0] = 1;
	for (int i = 1; i <= 200000; i++) fac[i] = fac[i - 1] * i * 1ll % mod;
}

ll combine(int n, int m) {
	if (n < m) return 0;
	else return fac[n] * quick_pow(fac[m], mod - 2) % mod * quick_pow(fac[n - m], mod - 2) % mod;
}

int main() {
	get_fac();
	cin >> n;
	for (int i = 1; i <= n; i++) {
		ll x; int k = 0;
		cin >> x;
		while (x) c[k++] += x & 1, x >>= 1;
	}

	ll bs = 1;
	ll res = 0;
	for (int i = 0; i < 64; i++) {
		res = (res + (combine(c[i], 3) % mod + (c[i] * combine(n - c[i], 2) % mod)) * bs) % mod;
		bs = (bs << 1) % mod;
	}
	cout << res;
}
根据用户提供的关键词“小A 弹吉他 网 小白 108 比详情 参攻略”,以下是整合后的相关信息建议: --- ### 关于小白108的比详情 小白是由网主办的一系列面向编程爱好者的在线竞之一。第108场事通常会围绕算法、数据结构以及实际问题解决能力展开挑战。比题目可能涉及但不限于字符串处理、动态规划、图论等领域。 对于与“小A弹吉他”相关的具体题目,可能是某道以音乐或乐器为主题的趣味性算法题。这类题目往往需要结合数学建模能力逻辑推理技巧来完成解答。 --- ### 如何准备此类比? #### 方法一:熟悉常见算法模板 确保掌握基础的数据结构(如栈、队列)及经典算法模型(例如深度优先搜索DFS、广度优先搜索BFS)。针对可能出现的音符序列匹配或者节奏计算等问题提前复习KMP模式匹配法等相关知识点。 #### 方法二:模拟真实考场环境练习 利用过往的小白记录进行刷题训练,在规定时间内尝试独立解决问题从而提升临场发挥水平。同时注意控制提交频率避免因超时错误而扣分过多。 #### 方法三:学习优秀选手思路分享 访问社区查看往届高排名玩家的经验贴。他们可能会提到如何快速理解复杂描述型试题的方法论;也可能提供一些特别好用但容易被忽略掉的小技巧比如调试输出设置等细节优化方案。 --- ### 示例代码片段供参考(假设存在一个简单版本的问题) 如果遇到类似判断两个旋律是否相同类型的程序设计,则可以考虑如下实现方式: ```python def is_same_melody(melody_a, melody_b): return melody_a == melody_b melody_A = list(map(int, input().split())) melody_B = list(map(int, input().split())) if len(melody_A) != len(melody_B): print("No") else: if is_same_melody(melody_A,melody_B): print("Yes") else: print("No") ``` 此段落仅为示意用途,请依据实际情况调整适应不同难度等级下的业务场景需求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值