Luogu P10778 BZOJ3569 DZY Loves Chinese II 题解 [ 紫 ] [ Xor Hashing ] [ 线性基 ] [ DFS 树 ]
一道喵喵哈希题。
对于连通性问题,可以考虑对原图建出 DFS 树,然后对于不同的边采取不同的处理方式。在本题中,如果需要把原图割开,那么至少需要存在一条树边,使得经过它的非树边全都被割开,且它自己被割开。
于是问题被转化为了判断给定边集是否为原图上部分特定边集的超集。
直接做显然是不好做的。我们考虑 Xor Hashing,为每条边随机赋权,然后记录下这 \(n - 1\) 个特殊边集所有边的异或和。对于给定边集的一次询问,我们需要判断是否存在一个子集的异或和与某个特殊边集的异或和相同,这个显然是可以线性基判断的。因为必须枚举每个特殊边集,所以此时时间复杂度为 \(O(qn\log V)\),无法通过。
考虑这个做法瓶颈在于哪里。显然是因为我们每个特殊边集的异或和是不同的,但是我们无需判断到底是哪个特殊边集与之匹配,而是只要存在一个与之匹配即可。于是我们考虑把所有特殊边集的异或和全部赋为同一个值:\(\bm 0\)。现在的问题就是该如何构造出这样的边权分配方案。
依然是从 DFS 树的角度考虑,分类讨论不同的边:
- 树边:用于控制最后的异或和为 \(0\)。一开始我们不给它赋值,待到其他边全部随机赋权完了以后,再来给他赋值以满足构造要求。
- 返祖边:随机赋权,并且对 DFS 树上的返祖链进行覆盖,全部异或上自己的权值。可以用树上差分实现。
- 前向边:不管它,因为他在作为返祖边的时候已经赋值一遍了。
- 横叉边:无向图没有横叉边。
容易证明,所有跨过树边 \(e\) 的返祖边都恰好覆盖了 \(e\) 一次,因此 \(e\) 的权值取最后异或和的值即可。
接下来只要判断给定边集是否存在一个异或和为 \(0\) 的非空子集即可。我们把给定边集的权值插入线性基中,如果存在一个数插入失败,则证明存在一个异或和为 \(0\) 的非空子集;否则这个线性基的秩等于插入元素个数,说明不存在满足要求的非空子集。
时间复杂度 \(O(n + m+ qk\log V)\)。注意构造的时候可以参考 Tarjan 求 EDCC 的写法,可以比较容易地判重边。
注意,该 Xor Hashing 的做法是基于给定边集大小的,因为一旦给定边集大小超过 \(\log V\),那么线性基会很容易满秩,导致无论连不连都能异或出 \(0\)。但是在给定边集大小较小的时候,这个做法是正确的。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 100005, M = 1000005;
const ll MXV = 1000000000000000000ll;
mt19937_64 rnd((unsigned) time(NULL));
ll rd(ll l, ll r)
{
return uniform_int_distribution<ll> (l, r) (rnd);
}
int n, m, q, h[N], eidx = 1;
struct Edge{
int v, ne;
ll w;
} e[M];
void add(int u, int v)
{
e[++eidx] = {v, h[u]};
h[u] = eidx;
}
int dcnt = 0, dfn[N], tot;
ll ew[N];
bitset<N> vis;
void dfs(int u, int ineg)
{
dfn[u] = ++tot;
vis[u] = 1;
dcnt++;
for(int i = h[u]; i ; i = e[i].ne)
{
int v = e[i].v;
if((i ^ 1) == ineg) continue;
if(vis[v])
{
if(dfn[v] > dfn[u]) continue;
e[i].w = e[i ^ 1].w = rd(1, MXV);
ew[u] ^= e[i].w;
ew[v] ^= e[i].w;
continue;
}
dfs(v, i);
ew[u] ^= ew[v];
}
e[ineg].w = e[ineg ^ 1].w = ew[u];
}
struct Linear_Base{
ll p[70];
void init()
{
memset(p, 0, sizeof(p));
}
bool insert(ll x)
{
for(int i = 63; i >= 0; i--)
{
if((x >> i) & 1)
{
if(p[i]) x ^= p[i];
else
{
p[i] = x;
return 1;
}
}
}
return 0;
}
} lnb;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
dfs(1, 0);
cin >> q;
int lastans = 0;
while(q--)
{
if(dcnt != n)
{
cout << "Disconnected\n";
continue;
}
int k;
cin >> k;
lnb.init();
bool res = 1;
while(k--)
{
int id;
cin >> id;
id ^= lastans;
res &= lnb.insert(e[2 * id].w);
}
lastans += res;
if(res == 0) cout << "Disconnected\n";
else cout << "Connected\n";
}
return 0;
}

浙公网安备 33010602011771号