1 条题解

  • 0
    @ 2026-4-19 1:42:59

    Upd on 2022.4.3 修改错误。

    P3447 [POI2006]KRY-Crystals

    POI 合集

    读完题目,纷繁复杂的限制让我们无从下手。从哪个条件作为突破口呢?肯定是最严格的异或限制。

    异或有性质 ab=cb=aca \oplus b = c\Rightarrow b = a\oplus c。对应到题目中,就是若序列 aan1n - 1 个数确定,则未定数只能等于所有确定数的异或和。这启发我们思考,有没有一种可能,未定数对应的限制 mim_i 非常大,以至于无论 n1n - 1 个数怎么选,它们的异或和总不大于 mim_i,这样我们就可以很方便地用乘法原理求出答案。

    实际上有可能!考虑第 ii 位,假设存在 pp 使得 mpm_p 这一位为 11,但 apa_p00,说明若所有数字在第 ii 位及其高位异或和为 00,那么无论其它数字低 i1i - 1 位怎么填都符合条件,因为 apa_p 在第 ii 位小于 mpm_p,所以接下来的低 i1i - 1 位没有限制。

    这启发我们直接枚举所有 apa_pmpm_p 的最短 LCP,即 存在 pp 使得 mpm_p 在第 ii 位为 11apa_p00,且 aa 高于第 ii 位的部分与 mm 相同 的位数 ii。这样,我们只需把任意一个 pp 的系数变为 11(因为它依赖于剩余数的异或和),剩下的系数用乘法原理乘起来即可。

    具体地,考虑我们关心什么:第 ii 位异或和为 00 且存在 pp。这启发我们设计 DP fj,k,lf_{j, k, l} 表示考虑到第 jj 个数,第 ii 位异或和为 kk 且是否存在 pp。根据乘法原理与实际意义转移:

    mjm_jii 位为 11,那么 aja_j 可以选择 0/10 / 1:选 00 的方案数为 2i2^i,选 11 的方案数为 mjm_j 在低 2i12 ^ {i - 1} 位的值加上 11,记作 cic_i,实际上就是 (mj&(2i1))+1(m_j \& (2 ^ i - 1)) + 1。从 l=0l = 0 转移到 l=1l = 1 时,系数为 11,表示钦定 jj 的系数为 11

    $$\begin{aligned} & f_{j, 0, 0} = f_{j - 1, 1, 0}\times c_j \\ & f_{j, 1, 0} = f_{j - 1, 0, 0}\times c_j \\ & f_{j, 0, 1} = f_{j - 1, 1, 1}\times c_j + f_{j - 1, 0, 1}\times 2 ^ i + f_{j - 1, 0, 0} \\ & f_{j, 1, 1} = f_{j - 1, 0, 1}\times c_j + f_{j - 1, 1, 1}\times 2 ^ i + f_{j - 1, 1, 0} \end{aligned}$$

    mjm_jii 位为 00,则只能选 00 且方案数为 cjc_j

    fj,k,l=fj1,k,l×cjf_{j, k, l} = f_{j - 1, k, l}\times c_j

    最后,若 mmii 高的位的异或和为 00 才能计算当前位贡献 fn,0,1f_{n, 0, 1},因为钦定了高位 aa 都取 mm。注意全零序列会被统计到,因此最后答案减 11。时间复杂度是优秀的 O(nlogm)\mathcal{O}(n\log m)

    总结一下,对于位运算相关 有限制 的计数题,首先考虑 最严格 的限制,并尽量 独立每一位,如果做不到就按顺序考虑每一位的限制。选择本题的原因是希望各位同学能够感受到这种按位枚举什么东西然后再 DP 的套路。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 50 + 5;
    unsigned long long n, ans, xs, m[N], f[2][2][2];
    int main() {
    	cin >> n;
    	for(int i = 1; i <= n; i++) scanf("%llu", &m[i]), xs ^= m[i];
    	ans = !xs, xs = 0;
    	for(int i = 31; i >= 0; i--) {
    		memset(f, 0, sizeof(f)), f[0][0][0] = 1;
    		for(int j = 1, p = 0, q = 1; j <= n; j++, swap(p, q)) {
    			unsigned long long coef = (m[j] & ((1 << i) - 1)) + 1;
    			xs ^= m[j] >> i + 1;
    			for(int c : {0, 1}) for(int d : {0, 1}) f[q][c ^ m[j] >> i & 1][d] = f[p][c][d] * coef;
    			if(m[j] >> i & 1) for(int c : {0, 1}) f[q][c][1] += (f[p][c][1] << i) + f[p][c][0];
    		}
    		if(!xs) ans += f[n & 1][0][1];
    		else break;
    	}
    	cout << ans - 1 << endl;
    	return 0;
    }
    
    • 1

    信息

    ID
    3177
    时间
    100ms
    内存
    64MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者