poj3694 Network[边双缩点+树剖/并查集]
首先同一个点双内部的加边肯定不影响。。所以先缩点成树,然后每次加一条边,这条对应的树上路径上所有边就都不是桥了,且每次操作独立作用,不相互影响(不过有可能本来一条边已经不是桥了又被标记了一次),所以每次相当对树链做一次链覆盖,统计未覆盖边。这个是链剖板子。。$O(N\log^2N)$
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define mst(x) memset(x,0,sizeof x)
#define dbg(x) cerr << #x << " = " << x <<endl
#define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=1e5+;
int n,m,q,cas,fir;
struct thxorz{
int head[N],nxt[N<<],to[N<<],tot;
inline void add(int x,int y){
to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
to[++tot]=x,nxt[tot]=head[y],head[y]=tot;
}
#define y to[j]
int dfn[N],low[N],cut[N<<],bel[N],tim,dcc;
void tarjan(int x,int las){
dfn[x]=low[x]=++tim;
for(register int j=head[x];j;j=nxt[j])if(j^(las^)){
if(!dfn[y]){
tarjan(y,j),MIN(low[x],low[y]);
if(low[y]>dfn[x])cut[j]=cut[j^]=;
}
else MIN(low[x],dfn[y]);
}
}
void dfs(int x){
bel[x]=dcc;//dbg2(x,dcc);
for(register int j=head[x];j;j=nxt[j])if(!cut[j]&&!bel[y])dfs(y);
}
#undef y
inline void clear(){mst(head),mst(dfn),mst(cut),mst(bel),tim=dcc=,tot=;}
}G;
struct uuzlovetree{
int head[N],nxt[N<<],to[N<<],tot;
inline void add(int x,int y){
to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
to[++tot]=x,nxt[tot]=head[y],head[y]=tot;
}
#define y to[j]
int fa[N],topfa[N],cnt[N],son[N],pos[N],dep[N],tim;
void dfs1(int x,int fat){//dbg(x);
fa[x]=fat,cnt[x]=,dep[x]=dep[fat]+;int tmp=-;
for(register int j=head[x];j;j=nxt[j])if(y^fat)dfs1(y,x),cnt[x]+=cnt[y],MAX(tmp,cnt[y])&&(son[x]=y);
}
void dfs2(int x,int topf){
topfa[x]=topf,pos[x]=++tim;if(!son[x])return;dfs2(son[x],topf);
for(register int j=head[x];j;j=nxt[j])if(y^fa[x]&&y^son[x])dfs2(y,y);
}
#undef y
int tag[N<<],sumv[N<<];
#define lc i<<1
#define rc i<<1|1
inline void pushup(int i){sumv[i]=sumv[lc]+sumv[rc];}
inline void pushdown(int i){
if(tag[i])tag[lc]=tag[rc]=,sumv[lc]=sumv[rc]=,tag[i]=;
}
void Build(int i,int L,int R){//dbg(i),dbg2(L,R);
if(L==R){sumv[i]=;tag[i]=;return;}
int mid=L+R>>;
Build(lc,L,mid),Build(rc,mid+,R);pushup(i);
}
void Update(int i,int L,int R,int ql,int qr){
if(ql<=L&&qr>=R){tag[i]=,sumv[i]=;return;}
int mid=L+R>>;
pushdown(i);
if(ql<=mid)Update(lc,L,mid,ql,qr);
if(qr>mid)Update(rc,mid+,R,ql,qr);
pushup(i);
}
#undef lc
#undef rc
inline void tree_update(int x,int y){
while(topfa[x]^topfa[y]){
if(dep[topfa[x]]<dep[topfa[y]])_swap(x,y);
Update(,,tim,pos[topfa[x]],pos[x]),x=fa[topfa[x]];
}
if(dep[y]<dep[x])_swap(x,y);
if(x^y)Update(,,tim,pos[x]+,pos[y]);
}
inline void clear(){mst(head),mst(tag),mst(sumv),mst(son),tim=tot=;}
}T; int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
while(read(n),read(m),n||m){
G.clear(),T.clear();
for(register int i=,x,y;i<=m;++i)read(x),read(y),G.add(x,y);
for(register int i=;i<=n;++i)if(!G.dfn[i])G.tarjan(i,);
for(register int i=;i<=n;++i)if(!G.bel[i])++G.dcc,G.dfs(i);
for(register int t=;t<=G.tot;t+=){
int x=G.to[t],y=G.to[t^];
if(G.bel[x]^G.bel[y])T.add(G.bel[x],G.bel[y]);//dbg2(G.bel[x],G.bel[y]);
}
T.dfs1(,);T.dfs2(,);
T.Build(,,T.tim);
read(q);if(fir)puts("");fir=;
printf("Case %d:\n",++cas);
for(register int i=,x,y;i<=q;++i){
read(x),read(y);T.tree_update(G.bel[x],G.bel[y]);
printf("%d\n",T.sumv[]);
}
}
return ;
}
链剖
发现自己数据结构学傻掉了。。完全不需要的说。。因为每次加边的时候这个链就是一个环了对不对。。然后实际上可以看成再缩成一个大点,然后每次询问只要暴力往上爬到lca就好了。。具体实现的话呢,使用并查集来合并的,爬过的所有点(不管是原点还是后来的缩的点)都合并起来,所谓的看成大点就是把所有点所在的这个集合的根设为这个块最顶端的点,这样既方便合并,也方便跳,每次虽然是暴力跳的,但是每跳一次就会合并一次,合并$n$次,需要跳的越来越少,所以爬是线性的。。。但是被两个$\log$的链剖吊打了?
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define mst(x) memset(x,0,sizeof x)
#define dbg(x) cerr << #x << " = " << x <<endl
#define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=1e5+;
int n,m,q,cas,fir,ans;
struct thxorz{
int head[N],nxt[N<<],to[N<<],tot;
inline void add(int x,int y){
to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
to[++tot]=x,nxt[tot]=head[y],head[y]=tot;
}
#define y to[j]
int dfn[N],low[N],cut[N<<],bel[N],tim,dcc;
void tarjan(int x,int las){
dfn[x]=low[x]=++tim;
for(register int j=head[x];j;j=nxt[j])if(j^(las^)){
if(!dfn[y]){
tarjan(y,j),MIN(low[x],low[y]);
if(low[y]>dfn[x])cut[j]=cut[j^]=;
}
else MIN(low[x],dfn[y]);
}
}
void dfs(int x){
bel[x]=dcc;//dbg2(x,dcc);
for(register int j=head[x];j;j=nxt[j])if(!cut[j]&&!bel[y])dfs(y);
}
#undef y
inline void clear(){mst(head),mst(dfn),mst(cut),mst(bel),tim=dcc=,tot=;}
}G;
struct uuzlovetree{
int head[N],nxt[N<<],to[N<<],tot;
inline void add(int x,int y){
to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
to[++tot]=x,nxt[tot]=head[y],head[y]=tot;
}
#define y to[j]
int fa[N],dep[N],n;
void dfs1(int x,int fat){//dbg(x);
fa[x]=fat,dep[x]=dep[fat]+;
for(register int j=head[x];j;j=nxt[j])if(y^fat)dfs1(y,x);
}
#undef y
int anc[N];
int Find(int x){return anc[x]==x?x:anc[x]=Find(anc[x]);}
inline void stostostostothxorzorzorzorz(int x,int y){
x=Find(x),y=Find(y);
while(x^y){
if(dep[x]<dep[y])_swap(x,y);
int tmp=Find(fa[x]);
anc[x]=tmp,x=tmp;
--ans;
}
}
inline void clear(){mst(head),tot=n=;}
}T; int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
while(read(n),read(m),n||m){
G.clear(),T.clear();
for(register int i=,x,y;i<=m;++i)read(x),read(y),G.add(x,y);
for(register int i=;i<=n;++i)if(!G.dfn[i])G.tarjan(i,);
for(register int i=;i<=n;++i)if(!G.bel[i])++G.dcc,G.dfs(i);
for(register int t=;t<=G.tot;t+=){
int x=G.to[t],y=G.to[t^];
if(G.bel[x]^G.bel[y])T.add(G.bel[x],G.bel[y]);//dbg2(G.bel[x],G.bel[y]);
}
T.dfs1(,);ans=G.dcc-;
for(register int i=;i<=G.dcc;++i)T.anc[i]=i;
read(q);if(fir)puts("");fir=;
printf("Case %d:\n",++cas);
for(register int i=,x,y;i<=q;++i){
read(x),read(y);
T.stostostostothxorzorzorzorz(G.bel[x],G.bel[y]);
printf("%d\n",ans);
}
}
return ;
}
dsu
poj3694 Network[边双缩点+树剖/并查集]的更多相关文章
- POJ3694 Network 边双缩点+LCA+并查集
辣鸡错误:把dfs和ldfs搞混...QAQ 题意:给定一个无向图,然后查询q次,求每次查询就在图上增加一条边,求剩余割边的个数. 先把边双缩点,然后预处理出LCA的倍增数组: 然后加边时,从u往上跳 ...
- Codeforces Gym 100814C Connecting Graph 树剖并查集/LCA并查集
初始的时候有一个只有n个点的图(n <= 1e5), 现在进行m( m <= 1e5 )次操作 每次操作要么添加一条无向边, 要么询问之前结点u和v最早在哪一次操作的时候连通了 /* * ...
- Educational Codeforces Round 51 (Rated for Div. 2) G. Distinctification(线段树合并 + 并查集)
题意 给出一个长度为 \(n\) 序列 , 每个位置有 \(a_i , b_i\) 两个参数 , \(b_i\) 互不相同 ,你可以进行任意次如下的两种操作 : 若存在 \(j \not = i\) ...
- BZOJ4399魔法少女LJJ——线段树合并+并查集
题目描述 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味: ...
- 2018.09.30 bzoj4025: 二分图(线段树分治+并查集)
传送门 线段树分治好题. 这道题实际上有很多不同的做法: cdq分治. lct. - 而我学习了dzyo的线段树分治+并查集写法. 所谓线段树分治就是先把操作分成lognlognlogn个连续不相交的 ...
- 【BZOJ2733】永无乡(线段树,并查集)
[BZOJ2733]永无乡(线段树,并查集) 题面 BZOJ 题解 线段树合并 线段树合并是一个很有趣的姿势 前置技能:动态开点线段树 具体实现:每次合并两棵线段树的时候,假设叫做\(t1,t2\), ...
- 洛谷P3402 【模板】可持久化并查集 [主席树,并查集]
题目传送门 可持久化并查集 n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 ...
- 洛谷P4121 [WC2005]双面棋盘(线段树套并查集)
传送门 先膜一下大佬->这里 据说这题正解是LCT,然而感觉还是线段树套并查集的更容易理解 我们对于行与行之间用线段树维护,每一行内用并查集暴力枚举 每一行内用并查集暴力枚举连通块这个应该容易理 ...
- 2019牛客暑期多校训练营(第八场)E:Explorer(LCT裸题 也可用线段树模拟并查集维护连通性)
题意:给定N,M,然后给出M组信息(u,v,l,r),表示u到v有[l,r]范围的通行证有效.问有多少种通行证可以使得1和N连通. 思路:和bzoj魔法森林有点像,LCT维护最小生成树. 开始和队友 ...
随机推荐
- js的new Date兼容iOS和Android
在写一个移动端的报名活动页面时,其中一个逻辑是:过了报名日期,“立即报名”按钮置灰,不允许报名:具体逻辑如下,在真机上测试的时候,Android端可以把按钮失效,iOS则是无法把按钮失效.后台返回的时 ...
- Reactor系列(八)concatMap有序映射
#java#reactor#comcatMap# 有序映射 视频讲解:https://www.bilibili.com/video/av79705356/ FluxMonoTestCase.java ...
- Own MusicPlayer隐私策略
本地音乐播放器 此为Own MusicPlayer本地音乐播放器的隐私策略,本隐私策略内容会不定期更新,以最新内容为主. 若您已经阅读并了解以下内容后,并继续使用该软件,即表示您已同意该协议. 访问权 ...
- (二)mybatis框架原理(图解)
目录 mybatis 框架原理图(粗略版) mybatis 框架原理图(粗略版)
- Ansible 自动化工具入门
Ansible 配置 Ansible 简单管理 下面的小实验,我们将采用以下的IP地址规划,请自行配置好. [实验环境] [状态] [主机列表] [python版本] 控制机 192.168.1.10 ...
- Java 私有构造函数的使用
被private修饰的私有构造函数无法在其他类中调用,也就是该类无法在其他类中实例化. 这种情况常用的使用场景:1.单例模式: 2.防止实例化. 一.单例模式 单例模式是一种常用的设计模式,思想是单例 ...
- 6-MySQL DBA笔记-查询优化
第6章 查询优化 查询优化是研发人员比较关注也是疑问较多的领域.本章首先为读者介绍常用的优化策略.MySQL的优化器.连接机制,然后介绍各种语句的优化,在阅读本章之前,需要先对EXPLAIN命令,索引 ...
- lua堆栈
lua堆栈 来源 https://blog.csdn.net/suhuaiqiang_janlay/article/details/56702381 来源 https://blog.csdn.net/ ...
- python之如何爬取一篇小说的第一章内容
现在网上有很多小说网站,但其实,有一些小说网站是没有自己的资源的,那么这些资源是从哪里来的呢?当然是“偷取”别人的数据咯.现在的问题就是,该怎么去爬取别人的资源呢,这里便从简单的开始,爬取一篇小说的第 ...
- 记录一次SourceTree无法push问题排查及解决
1.push代码卡住,一直转圈2.试了下拉取代码也拉不到3.试了使用git命令行push可以4.使用Sourcetree新建项目,一直在检查url.5.初步判断原因,SourceTree无法联网.6. ...