CF2022E 题解 | 数学、并查集
标签:数学、并查集
题意
给你一个 \(n\) 行 \(m\) 列的矩阵 \(a\),满足限定条件:
对于 \(\forall i_1, i_2, j_1, j_2, a_{i_1, j_1} \oplus a_{i_2, j_1} \oplus a_{i_1, j_2} \oplus a_{i_2, j_2} = 0\)。
换句话说,就是所有的子矩阵都满足四个角上的值异或和为 \(0\)。
同时,给出 \(k\) 个约束,形如 \(a_{r, c} = v\)。
给出 \(q\) 个持久化修改,形如 \(a_{r, c} = v\)。
思路
即,给定 \(i_1, i_2\),这两行中同一列的两个数异或和是定值,也就是不受到 \(j\) 的影响。
同时结合异或的性质,我们已经可以猜想:构造一个数列 \(Y_{1,...,m}\),\(a_{i,j}\) 是 \(Y_{j}\) 异或另一些值得到的。
同理,我们将行和列换过来,可以得到类似的直觉,于是大胆猜想:\(a_{i,j}=X_{i} \oplus Y_{j}\)。
经过验证,这是满足题目限定的一种构造。
证明:\(a_{i,j}=X_{i} \oplus Y_{j}\):
观察这个式子,肯定要将一个坐标和行、列结合。
又是异或运算,容易想到 \(a'_{i,j} \leftarrow a_{i,j} \oplus a_{i,1} \oplus a_{1,j}\)。然后我们发现,这样之后得到的新的 \(a'\) 依然是满足条件的矩阵。
证明:
每个子矩阵的表达式都异或上了 \(a_{i_1, 1}, a_{i_2, 1}, a_{1, j_1}, a_{1, j_2}\) 且都异或两遍。
那么,考虑一个左上角的矩阵(即 \(i_1 = j_1 = 1\)):
\[\because a'_{1, 1} = a'_{1, j_2} = a'{i_2, 1} = a_{1, 1} \]\[\therefore a'_{i_2, j_2} = a_{1, 1}, \therefore a_{i_2, j_2} = a_{1, 1} \oplus a_{i_2, 1} \oplus a_{1, j_2} \]于是已经基本证明完了,给出一个构造:
构造 \(X_{i} = a_{i, 1} \oplus a_{1, 1}, Y_{j} = a_{1, j}\) 即可。
这样的话,就可以应用方格图常用套路,将横纵坐标连边。含义为:若一个点的值(对应 \(X\) 或 \(Y\))确定,则另一个点确定。
那么对于限制 \(a_{r, c} = v\),将边权设置为 \(v\) 即可。
判定无解的条件即为:存在 \(u, v\),存在两条从 \(u\) 到 \(v\) 的路径,且路径上的异或和不同。
转换为存在一个异或和非 \(0\) 的环。
若有解,则每个联通块的可能性即为 \(V = 2 ^ {30}\),那么总共的序列 \(X\) 和 \(Y\) 有 \(2 ^ {{30} ^ {cnt}}\) 种,其中 \(cnt\) 为联通块个数。
而每 \(2 ^ {30}\) 组 \(X\)、\(Y\) 的取值会对应相同的一个矩阵 \(a\)。(将 \(X\)、\(Y\) 同时异或一个 \(p, p \in [0, 2 ^ {30} - 1]\) 不会影响 \(a\))
于是答案为 \(2 ^ {{30} ^ {cnt - 1}}\)。
由于修改是持久的,拓展域带权并查集维护即可。
代码
const int N = 2e5 + 5;
const int mod = 1e9 + 7;
int n, m, k, q;
int cnt = 0;
bool flg = 1;
ll qpow(ll a, int b){
ll res = 1;
while(b){
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
struct dsu{
int fa[N], val[N], siz[N];
void init(int n){
rep(i, 1, n) fa[i] = i, val[i] = 0;
}
int find(int x){
if(fa[x] == x) return x;
int top = find(fa[x]);
val[x] ^= val[fa[x]];
fa[x] = top;
return top;
}
bool merge(int u, int v, int w){
int fu = find(u), fv = find(v);
if(fu == fv){
if((val[u] ^ val[v] ^ w) != 0){
return 0;
}
else return 1;
}
if(siz[fu] > siz[fv]) swap(u, v), swap(fu, fv);
fa[fu] = fv;
val[fu] = val[u] ^ w ^ val[v];
cnt--;
return 1;
}
} dsu;
ll calc(){
if(!flg) return 0ll;
return qpow((1ll << 30), cnt - 1);
}
void solve_test_case(){
n = read(), m = read(), k = read(), q = read();
flg = 1, cnt = n + m, dsu.init(n + m);
rep(i, 1, k){
int r = read(), c = read(), v = read();
if(!dsu.merge(r, c + n, v)) flg = 0;
}
write(calc());
rep(i, 1, q){
int r = read(), c = read(), v = read();
if(!dsu.merge(r, c + n, v)) flg = 0;
write(calc());
}
}
signed main(){
#ifdef FILE_NAME
freopen((string(MACRO_STR(FILE_NAME)) + ".in").c_str(), "r", stdin);
freopen((string(MACRO_STR(FILE_NAME)) + ".out").c_str(), "w", stdout);
#endif
int Test_case_num = read();
while(Test_case_num--) solve_test_case();
return 0;
}

浙公网安备 33010602011771号