bzoj3569: DZY Loves Chinese II 并查集 随机化 解决动态图连通性问题

bzoj3569: DZY Loves Chinese II

Description

神校XJ之学霸兮,Dzy皇考曰JC。
摄提贞于孟陬兮,惟庚寅Dzy以降。
纷Dzy既有此内美兮,又重之以修能。
遂降临于OI界,欲以神力而凌♂辱众生。
今Dzy有一魞歄图,其上有N座祭坛,又有M条膴蠁边。
时而Dzy狂WA而怒发冲冠,神力外溢,遂有K条膴蠁边灰飞烟灭。
而后俟其日A50题则又令其复原。(可视为立即复原)
然若有祭坛无法相互到达,Dzy之神力便会大减,于是欲知其是否连通。

Input

第一行N,M
接下来M行x,y:表示M条膴蠁边,依次编号
接下来一行Q
接下来Q行:
每行第一个数K而后K个编号c1~cK:表示K条边,编号为c1~cK
为了体现在线,c1~cK均需异或之前回答为连通的个数

Output

对于每个询问输出:连通则为‘Connected’,不连通则为‘Disconnected’
(不加引号)

Sample Input

5 10
2 1
3 2
4 2
5 1
5 3
4 1
4 3
5 2
3 1
5 4
5
1 1
3 7 0 3
4 0 7 4 6
2 2 7
4 5 0 2 13

Sample Output

Connected
Connected
Connected
Connected
Disconnected

HINT

N≤100000 M≤500000 Q≤50000 1≤K≤15
数据保证没有重边与自环
Tip:请学会使用搜索引擎

分析

题目大意:询问一张给定图去掉若干条边后是否联通,强制在线。
因为强制在线,cdq分治的做法不行了,于是有一种神奇的操作。
首先随便生成一棵树。
图不联通意味着一定有某棵子树脱离了原图。
一棵子树脱离原树意味着子树根和父亲之间的边和通向子树外的所有非树边都被选中了。
对于每条边随机赋值。定义一条树边的权值是其子树内所有通往子树外的边的异或和(不包括父亲边)。
这样子的话,询问变成了若干个数。可以发现,在随机意义下,存在某些数异或和为零和图不联通是等价的。
因为存在异或和为零证明有一条树边和这条树边和通往子树根外部的所有非树边被选中。
非常优秀的 O(nlogL) O ( n l o g L ) 算法。

代码

#include<cstdio>
#include<ctime>
#include<cstdlib>
const int N = 1e5 + 10, M = 5e5 + 10;
int ri() {
    char c = getchar(); int x = 0; for(;c < '0' || c > '9'; c = getchar()) ;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x;
}
bool v[N]; int nx[M], to[M], b[N], pr[N], a[M], c[31], bin[31], tp;
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
void Dfs1(int u) {
    v[u] = 1;
    for(int i = pr[u]; i; i = nx[i])
        !v[to[i]] ? Dfs1(to[i]), 0 : (a[i] = rand(), b[u] ^= a[i], b[to[i]] ^= a[i]);
}
void Dfs2(int u) {
    v[u] = 0;
    for(int i = pr[u]; i; i = nx[i])
        v[to[i]] ? Dfs2(to[i]), a[i] ^= b[to[i]], b[u] ^= b[to[i]] : 0;
}
bool Ad(int x) {
    for(int i = 30; ~i && ~x && x; --i)
        if(x & bin[i]) c[i] ? x ^= c[i] : (c[i] = x, x = -1); 
    return ~x;
}
int main() {
    srand(20010927); int n = ri(), m = ri();
    bin[0] = 1; for(int i = 1; i <= 30; ++i) bin[i] = bin[i - 1] << 1;
    for(int i = 1, u, v;i <= m; ++i) u = ri(), v = ri(), u > v ? add(v, u) : add(u, v);
    Dfs1(1); Dfs2(1); int la = 0; bool f = false;
    for(int T = ri();T--; f = false) {
        for(int k = ri();k--;) !f ? f = Ad(a[ri() ^ la]) : ri();
        for(int i = 0;i <= 30; ++i) c[i] = 0;
        puts(f ? "Disconnected" : "Connected"); la += !f;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值