2024ccpc线性基与校赛线性基
异或空间线性基
我终于意识到写题解有多重要了
2024 CCPC 网络赛 Problem J. 找最小
Mandy 发现了两个很好玩的长度为
现在她想要这两个序列尽可能有趣,具体来说,她希望最无趣的序列尽可能有趣。她觉得交换两个序列中对应位置的元素是无伤大雅的,可以进行任意次这样的操作。
现在她想要知道,最有趣的情况下两个序列无趣度较大者的无趣度是多少呢?
形式化地来说,你有两个长度为
Output
第一行一个整数
对于每组数据,第一行一个整数
第二行
第三行
保证对于所有数据,
Solution
首先有一个重要观察,如果当前两个数组的异或和分别是
即
于是可以考虑线性基,设异或出的数字为
问题进展到这里,我们对
而线性基刚好异或上这一个,那么这个对应的这一位就在结果中出现,不异或这一个,就不会出现。
接下来就是分类讨论了,当前位若
下面是参考代码
点击查看代码
// Created by qyy on 2024/9/9.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int, int>
#define endl "\n"
const long long inf = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
int n;
ll a[N], b[N];
bool zero;
ll p[N];
int xxjcnt;
int Gauss(){
// 此构造线形基出的是降序排序,大小为 xxjcnt 个,编号从 1 开始
for(int i = 1; i <= n; i++){
p[i] = a[i];
}
int i, k = 1;
ll j = (ll) 1 << 62; // 注意不是 63;
for(; j; j >>= 1){
for(i = k; i <= n; i++) {
if (p[i] & j) {
break; // 找到了第 j 位上的 1
}
}
if(i > n){
continue; // 没有找到第 j 位上的 1
}
swap(p[i], p[k]);
for(i = 1; i <= n; i++){
if(i != k && p[i] & j){
p[i] ^= p[k];
}
}
k++;
}
k--;
if(k != n){
zero = true;
}else{
zero = false;
}
return k;
}
ll toj[100]; // 需要更新;
int cal(ll x){
for(int i = 62; i >= 0; i--){
if((1LL << i) & x){
return i;
}
}
}
void solve() {
cin >> n;
for(int i = 0; i < 90; i++){
toj[i] = 0;
}
ll A = 0, B = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
A ^= a[i];
}
for(int i = 1; i <= n; i++){
cin >> b[i];
B ^= b[i];
}
for(int i = 1; i <= n; i++){
a[i] ^= b[i];
}
if(A < B){
swap(A, B);
}
xxjcnt = Gauss();
for(int i = 1; i <= xxjcnt; i++){
int x = cal(p[i]);
toj[x] = p[i];
}
ll ans1 = 0, ans2 = 0;
bool flag = false;
for(int i = 62; i >= 0; i--){
ll x = (1LL << i);
if((x&A) && (!(x&B))){
// 开始分类;
if(toj[i] != 0){
if(flag){
ans2 ^= toj[i]; // 应该按照 B 小的走,因为现在 B 异或出来一定比 A 大
}else{
ans1 ^= toj[i]; // 应该按照 B 小的走,因为现在 B 异或出来一定比 A 大
}
}
flag = true;
}else if((x&A) && (x&B)){
// 需要 toj[i] 参与进来
if(toj[i] != 0){
ans1 ^= toj[i];
ans2 ^= toj[i];
}
}else if((!(A&x)) && (!(B&x))){
// 不需要任何参与
}else{
// 给两个答案都计算上
if(toj[i] != 0){
ans1 ^= toj[i];
}
}
}
ll res1 = max(A^ans1, B^ans1);
ll res2 = max(A^ans2, B^ans2);
cout << min(res1, res2) << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
2024 校赛(团队赛)
作为 xxcdsg 的舔狗,Taibo 经常邀请 xxcdsg 一起玩无聊的游戏。
有一个长度为
现在,Taibo 想要最大化这个得分,而 xxcdsg 则想要最小化该得分。如果双方都采取最佳策略的情况下,最终的得分将是多少?关于非空子序列的定义:如果正整数
例如,[1,2,6],[1,3,5],[4],[1,3,5,2,4,6]是[1,3,5,2,4,6]的非空子序列,而[1,2,3],[4,5],[1,2,3,4,5,6]不是。
solution
首先一个重要观察,不管 Taibo 第一轮选了什么,剩下的所有数 xxcdsg 一定会全部全部拿光。原因即异或是不进位加法,
观察到数据范围,线性基后,可以搜索出每一种可能,于是枚举取最大值就行了。
参考代码如下:
点击查看代码
// Created by qyy on 2024/9/9.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int, int>
#define endl "\n"
const long long inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
const int mod = 7;
int n;
ll a[N];
bool zero;
ll p[N];
int xxjcnt;
int Gauss(){
// 此构造线形基出的是降序排序,大小为 xxjcnt 个,编号从 1 开始
for(int i = 1; i <= n; i++){
p[i] = a[i];
}
int i, k = 1;
ll j = (ll) 1 << 62; // 注意不是 63;
for(; j; j >>= 1){
for(i = k; i <= n; i++) {
if (p[i] & j) {
break; // 找到了第 j 位上的 1
}
}
if(i > n){
continue; // 没有找到第 j 位上的 1
}
swap(p[i], p[k]);
for(i = 1; i <= n; i++){
if(i != k && p[i] & j){
p[i] ^= p[k];
}
}
k++;
}
k--;
if(k != n){
zero = true;
}else{
zero = false;
}
return k;
}
ll ans = 0, sum = 0;
void dfs(int pos, ll cur){
if(pos == xxjcnt + 1){
ans = max(ans, cur + (sum^cur));
return ;
}
dfs(pos + 1, cur);
cur ^= p[pos];
dfs(pos + 1, cur);
}
void solve() {
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
sum ^= a[i];
}
xxjcnt = Gauss();
ans = sum;
dfs(1, 0);
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
//cin >> t;
while (t--) {
solve();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)