Loading

线性基

P3812 【模板】线性基

题目描述

给定 \(n\) 个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。

提示

\(1 \leq n \leq 50, 0 \leq S_i < 2 ^ {50}\)

注意开long long

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
using i64 = long long;

const int N = 70;

i64 p[N];

void insert(i64 x)
{
	for (int i = 63; i >= 0; i--)
	{
		if (x >> i & 1)
		{
			if (!p[i])
			{
				p[i] = x;
				break;		
			}
			else
			{
				x ^= p[i];
			}
		}
	}
}

i64 getans()
{
	i64 res = 0;
	for (int i = 63; i >= 0; i--)
		if ((res ^ p[i]) > res)
			res ^= p[i];
	return res;
}

int n;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		i64 x;
		cin >> x;
		insert(x);
	}
	
	cout << getans() << "\n";
	return 0;
}

HDU-3949 XOR

P3812 的进阶版。

注意1 << 63 会炸,要使用1ll << 631ll << 63ll

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

#define int long long

const int N = 70;

int p[N];

inline void insert(int x)
{
	for (int i = 63; i >= 0; i--)
	{
		if (x >> i & 1)
		{
			if (!p[i])
			{
				p[i] = x;
				break;
			}
			else x ^= p[i];
		}
	}
}

inline void solve()
{
	int n;
	cin >> n;
	memset(p, 0, sizeof(p));
	for (int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		insert(x);
	}
	
	for (int i = 0; i <= 63; i++)
		for (int j = i + 1; j <= 63; j++)
			if (p[j] >> i & 1)
				p[j] ^= p[i];
				
	vector<int> res;
	for (int i = 0; i <= 63; i++) if (p[i]) res.push_back(p[i]);
	int sz = res.size();
	int q;
	cin >> q;
	while (q--)
	{
		int x, ans = 0;
		cin >> x;
		if (n > sz) x--;
		if ((1ll << sz) - 1ll < x) ans = -1;
		else for (int i = 63; i >= 0; i--) if (x >> i & 1) ans ^= res[i];
		cout << ans << '\n';
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int T;
	cin >> T;
	for (int i = 1; i <= T; i++)
	{
		cout << "Case #" << i << ":\n";
		solve();
	}
	return 0;
}

P3857 [TJOI2008]彩灯

与 HDU-3949 相同,一个是统计第 \(k\) 小,一个是统计个数。

实际上就是 \(p\) 数组中有值的数字个数 \(cnt\) (即 \(\sum[p[i] \neq 0]\)), 答案为 \(2^{cnt}\)

#include <iostream>
#include <cstring>
#include <algorithm>

#define int long long

using namespace std;

const int N = 64;

int n, m;
int p[N];

void insert(int x)
{
	for (int i = 63; i >= 0; i--)
	{
		if (x >> i & 1)
		{
			if (!p[i])
			{
				p[i] = x;
				break;
			}
			else x ^= p[i];
		}
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cin >> m >> n;
	for (int i = 1; i <= n; i++)
	{
		int t = 0;
		for (int j = 1; j <= m; j++)
		{
			char ch;
			cin >> ch;
			t = t << 1 | (ch == 'O');
		}
		// cout << t << '\n';
		insert(t);
	}
	
	for (int i = 0; i <= 63; i++)
		for (int j = i + 1; j <= 63; j++)
			if (p[j] >> i & 1) p[j] ^= p[i];
	
	int cnt = 0;
	for (int i = 0; i <= 63; i++) if (p[i]) cnt++;
	cout << (1ll << cnt) % 2008ll << '\n';
	return 0;
}

P4570 [BJWC2011]元素

贪心,按 \(Magic_i\) 从大到小排序,再将 \(Number_i\) 插入线性基,如果插入成功,就表示不会发生冲突,答案 \(ans\) 加上 \(Magic_i\);否则跳过。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
using i64 = long long;
using PII = pair<i64, i64>;

const int N = 1010, M = 64;

i64 p[M];
PII a[N];
int n;

bool insert(i64 x)
{
	for (int i = 63; i >= 0; i--)
	{
		if (x >> i & 1)
		{
			if (!p[i])
			{
				p[i] = x;
				return true;
			}
			else x ^= p[i];
		}
	}
	return false;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i].first >> a[i].second;
	sort(a + 1, a + n + 1, [](const PII& x, const PII& y){ return x.second > y.second; });
	
	i64 ans = 0;
	for (int i = 1; i <= n; i++)
		if (insert(a[i].first))
			ans += a[i].second;
	
	cout << ans << '\n';
	return 0;
}

CF845G Shortest Path Problem?

这题每一条路径相当于 \(一条链 + 几个环\)

那么此题的思路为:

  1. 随便找一条链,链上所有的值异或起来,记为 \(ans\)

  2. 在把所有环上的异或值插入线性基。

然后再在线性基中随便找一些数异或 \(ans\),使其最大就行了,成功转化为模板题。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010, M = 200010;

struct Edge
{
	int to;
	int next;
	int w;
}e[M];

int head[N], idx;

void add(int a, int b, int c)
{
	idx++, e[idx].to = b, e[idx].next = head[a], e[idx].w = c, head[a] = idx;
	idx++, e[idx].to = a, e[idx].next = head[b], e[idx].w = c, head[b] = idx;
}

int n, m;
int sumx[N];						// sumx[i] : 1 ~ i xor
bool vis[N];						// visited ?
int p[70];

void insert(int x)
{
	// cout << x << '\n';
	for (int i = 31; i >= 0; i--)
	{
		if (x >> i & 1)
		{
			if (!p[i])
			{
				p[i] = x;
				break;
			}
			else x ^= p[i];
		}
	}
}

void dfs(int u, int x)
{
	sumx[u] = x, vis[u] = true;
	for (int i = head[u]; i; i = e[i].next)
	{
		int to = e[i].to;
		if (!vis[to]) dfs(to, x ^ e[i].w);
		else insert(x ^ e[i].w ^ sumx[to]);
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
	}
	dfs(1, 0);
	int res = sumx[n];
	for (int i = 31; i >= 0; i--)
		if ((res ^ p[i]) < res)
			res ^= p[i];
	
	printf("%d\n", res);
	return 0;
}

P3292 [SCOI2016]幸运数字

注意:

  1. 两个线性基是可合并的,合并可以参考代码中的 \(merge\)

  2. 插入线性基的时候最好判断一下该数是否为 \(0\),否则你爆T了。

于是,此题在暴力(30分)的基础上加个 \(LCA+RMQ\) 就可以了。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
using i64 = long long;

const int N = 20010, M = 40010;

struct Edge
{
	int to;
	int next;
}e[M];

int head[N], idx;

void add(int a, int b)
{
	idx++, e[idx].to = b, e[idx].next = head[a], head[a] = idx;
	idx++, e[idx].to = a, e[idx].next = head[b], head[b] = idx;
}

int n, q;
bool vis[N];
i64 p[N][20][64];
int d[N];
int f[N][20];

void insert(i64 x, i64 c[64])
{
	for (int i = 63; i >= 0; i--)
	{
		if (x >> i & 1)
		{
			if (!c[i])
			{
				c[i] = x;
				break;
			}
			else x ^= c[i];
		}
	}
}

void merge(i64 a[64], i64 b[64])
{
	for (int i = 63; i >= 0; i--) if (b[i]) insert(b[i], a);
}

void dfs(int u, int fa)
{
	d[u] = d[fa] + 1;
	f[u][0] = fa;
	
	for (int i = head[u]; i; i = e[i].next)
	{
		int to = e[i].to;
		if (to != fa) dfs(to, u);
	}
}

void prepare()
{
	for (int j = 1; j < 20; j++)
		for (int i = 1; i <= n; i++)
		{
			f[i][j] = f[f[i][j - 1]][j - 1];
			for (int k = 0; k <= 63; k++) p[i][j][k] = p[i][j - 1][k];
			merge(p[i][j], p[f[i][j - 1]][j - 1]);
		}
}

void lca(int x, int y, i64 res[64])
{
	for (int i = 0; i <= 63; i++) res[i] = 0;
	if (d[x] < d[y]) swap(x, y);
	for (int i = 19; i >= 0; i--)
		if (d[f[x][i]] >= d[y])
		{
			merge(res, p[x][i]);
			x = f[x][i];
		}
	
	if (x == y)
	{
		merge(res, p[x][0]);
		return;
	}
	
	for (int i = 19; i >= 0; i--)
		if (f[x][i] != f[y][i])
		{
			merge(res, p[x][i]);
			merge(res, p[y][i]);
			x = f[x][i], y = f[y][i];
		}
	
	merge(res, p[x][1]);
	merge(res, p[y][1]);
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n >> q;
	for (int i = 1; i <= n; i++)
	{
		i64 x;
		cin >> x;
		insert(x, p[i][0]);
	}
	for (int i = 1; i < n; i++)
	{
		int a, b;
		cin >> a >> b;
		add(a, b);
	}
	dfs(1, 0);
	prepare();
	i64 tmp[64];
	while (q--)
	{
		int a, b;
		cin >> a >> b;
		lca(a, b, tmp);
		i64 res = 0;
		for (int i = 63; i >= 0; i--)
			if ((res ^ tmp[i]) > res)
				res ^= tmp[i];
		cout << res << '\n';
	}
	return 0;
}
posted @ 2022-12-02 22:14  SunnyYuan  阅读(18)  评论(0)    收藏  举报