其他分享
首页 > 其他分享> > UOJ #703 - 赵云八卦阵(线性基+dp)

UOJ #703 - 赵云八卦阵(线性基+dp)

作者:互联网

UOJ 题面传送门

首先考虑对于每个 \(i\),\(b_i\) 可能的取值范围是什么,不难发现 \(b_i\in\{x|x=\operatorname{xor}\limits_{y\in S}y,S\subseteq\{1,2,3,\cdots,i\},i\in S\}\),换句话,\(b_i\) 可能的取值范围为 \(\{1,2,3,\cdots,i-1\}\) 某个自己的异或和异或上 \(a_i\)。

由于涉及到子集的异或和,我们很自然地想到线性基,更具体地,我们将 \(a_1,a_2,\cdots,a_n\) 依次插入线性基,那么如果插入一个 \(a_i\) 时,线性基维数增大,那么对应的 \(b_i\) 的取值范围就是线性基中新增的这部分元素,否则 \(b_i\) 的取值范围就是此时此刻线性基中的元素。

注意到一个性质:如果我们称“插入 \(a_i\) 时,线性基大小发生改变”的 \(a_i\) 为关键点,那么关键点最多有 \(\log V\) 个,我们还发现,对于非关键点,其在 LIS 中的部分一定是一段后缀,因为对于非关键点而言,前面的部分的取值范围一定严格包含于前面的部分,我们肯定会选择尽量将这些非关键点上的值靠后放,这样答案一定不会变劣。

这样一来我们就可以只用 DP 关键点的位置即可,即,设关键点的位置为 \(p_1,p_2,\cdots,p_m\)​,那么我们设 \(dp_{i,j}\)​ 表示我们钦定了 \(p_i\sim n\)​ 的取值,目前 \(p_i,p_{i+1},\cdots,p_m\)​ 中有 \(j\)​ 个位置在 LIS 上,LIS 开头元素的最大值。转移相当于先加入一段非关键点,再加入一个关键点,非关键点的部分就找出 \(dp_{i,j}\)​ 在 \([1,p_{i}-1]\)​ 处前缀线性基中的排名,然后往前数 \(len\)​ 个再转化成数值,其中 \(len\)​ 表示非关键点个数,如果数到头了就直接更新答案并令 \(dp_{i,j}=0\)​。关键点的部分相当于这样一个问题:给定一个线性基 \(\mathcal B\)​ 和两个数 \(X,Y\)​,求 \(X\oplus x\)​ 的最大值,满足 \(X\oplus x<Y\)​,且 \(x\)​ 可以被 \(\mathcal B\)​ 表示出来。这是一个经典问题,先找到 \(\mathcal B\)​ 的最小基 \(c_1,c_2,\cdots,c_k(c_i<c_{i+1})\)​,然后按下标递增的顺序考虑所有 \(c_i\)​,如果 \(X\oplus c_i<X\)​ 就令 \(X\)​ 异或上 \(c_i\)​​。再按下标递增的顺序考虑所有 \(c_i\),如果 \(X\oplus c_i<Y\) 就令 \(X\) 异或上 \(c_i\)。

时间复杂度 \(n\log V+\log^3V\),瓶颈在于一开始将所有 \(a_i\) 插入线性基求关键点的位置。

using namespace fastio;
const int MAXN = 1e6;
const int LOG_V = 60;
int n; ll a[MAXN + 5], bs[LOG_V + 2][LOG_V + 2], b[LOG_V + 2];
int tot = 0, cc[LOG_V + 2], cnt[LOG_V + 2]; ll val[LOG_V + 2], dp[LOG_V + 2];
bool ins(ll v) {
	for (int i = LOG_V; ~i; i--) if (v >> i & 1) {
		if (!b[i]) {
			b[i] = v;
			for (int j = i - 1; ~j; j--) if (b[i] >> j & 1) b[i] ^= b[j];
			for (int j = i + 1; j <= LOG_V; j++) if (b[j] >> i & 1) b[j] ^= b[i];
			return 1;
		} else v ^= b[i];
	}
	return 0;
}
ll getrnk(int sz, ll *B, ll v) {
	ll cur = 0, rk = 0;
	for (int i = sz - 1; ~i; i--) if ((cur ^ B[i]) < v)
		cur ^= B[i], rk += 1ll << i;
	return rk;
}
ll getkth(int sz, ll *B, ll k) {
	ll cur = 0;
	for (int i = sz - 1; ~i; i--) if (k >> i & 1) cur ^= B[i];
	return cur;
}
ll getnxt(int sz, ll *B, ll cur, ll nd) {
	for (int i = sz - 1; ~i; i--) if ((cur ^ B[i]) < cur) cur ^= B[i];
	if (cur >= nd) return 0;
	for (int i = sz - 1; ~i; i--) if ((cur ^ B[i]) < nd) cur ^= B[i];
	return cur;
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++) read(a[i]);
	for (int i = 1; i <= n; i++) {
		if (ins(a[i])) {
			tot++;
			for (int j = 0; j <= LOG_V; j++) if (b[j])
				bs[tot][cc[tot]++] = b[j];
			val[tot] = a[i];
		} else cnt[tot]++;
	}
//	printf("%d\n", tot);
	dp[0] = LLONG_MAX; int ss = 0, res = 0;
	for (int i = tot; i; i--) {
		if (cnt[i]) {
			for (int j = 0; j <= LOG_V; j++) if (dp[j]) {
				ll rk = getrnk(cc[i], bs[i], dp[j]);
//				printf("! %d %d %lld\n", i, j, rk);
				if (rk < cnt[i]) {
					chkmax(res, rk + j + ss + 1);
					dp[j] = 0;
				} else {
					dp[j] = getkth(cc[i], bs[i], rk - cnt[i] + 1);
//					printf("!! %lld\n", dp[j]);
				}
			}
		}
		for (int j = LOG_V; ~j; j--) if (dp[j]) {
			chkmax(dp[j + 1], getnxt(cc[i - 1], bs[i - 1], val[i], dp[j]));
		}
//		for (int j = 0; j <= tot; j++) printf("%lld%c", dp[j], " \n"[j == tot]);
		ss += cnt[i];
	}
	for (int i = 0; i <= tot; i++) if (dp[i]) chkmax(res, ss + i);
	printf("%d\n", res);
	return 0;
}

标签:cur,int,ll,703,LOG,线性,UOJ,dp,关键点
来源: https://www.cnblogs.com/ET2006/p/UOJ-703.html