首先同一个点双内部的加边肯定不影响。。所以先缩点成树,然后每次加一条边,这条对应的树上路径上所有边就都不是桥了,且每次操作独立作用,不相互影响(不过有可能本来一条边已经不是桥了又被标记了一次),所以每次相当对树链做一次链覆盖,统计未覆盖边。这个是链剖板子。。$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[边双缩点+树剖/并查集]的更多相关文章

  1. POJ3694 Network 边双缩点+LCA+并查集

    辣鸡错误:把dfs和ldfs搞混...QAQ 题意:给定一个无向图,然后查询q次,求每次查询就在图上增加一条边,求剩余割边的个数. 先把边双缩点,然后预处理出LCA的倍增数组: 然后加边时,从u往上跳 ...

  2. Codeforces Gym 100814C Connecting Graph 树剖并查集/LCA并查集

    初始的时候有一个只有n个点的图(n <= 1e5), 现在进行m( m <= 1e5 )次操作 每次操作要么添加一条无向边, 要么询问之前结点u和v最早在哪一次操作的时候连通了 /* * ...

  3. Educational Codeforces Round 51 (Rated for Div. 2) G. Distinctification(线段树合并 + 并查集)

    题意 给出一个长度为 \(n\) 序列 , 每个位置有 \(a_i , b_i\) 两个参数 , \(b_i\) 互不相同 ,你可以进行任意次如下的两种操作 : 若存在 \(j \not = i\) ...

  4. BZOJ4399魔法少女LJJ——线段树合并+并查集

    题目描述 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味: ...

  5. 2018.09.30 bzoj4025: 二分图(线段树分治+并查集)

    传送门 线段树分治好题. 这道题实际上有很多不同的做法: cdq分治. lct. - 而我学习了dzyo的线段树分治+并查集写法. 所谓线段树分治就是先把操作分成lognlognlogn个连续不相交的 ...

  6. 【BZOJ2733】永无乡(线段树,并查集)

    [BZOJ2733]永无乡(线段树,并查集) 题面 BZOJ 题解 线段树合并 线段树合并是一个很有趣的姿势 前置技能:动态开点线段树 具体实现:每次合并两棵线段树的时候,假设叫做\(t1,t2\), ...

  7. 洛谷P3402 【模板】可持久化并查集 [主席树,并查集]

    题目传送门 可持久化并查集 n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 ...

  8. 洛谷P4121 [WC2005]双面棋盘(线段树套并查集)

    传送门 先膜一下大佬->这里 据说这题正解是LCT,然而感觉还是线段树套并查集的更容易理解 我们对于行与行之间用线段树维护,每一行内用并查集暴力枚举 每一行内用并查集暴力枚举连通块这个应该容易理 ...

  9. 2019牛客暑期多校训练营(第八场)E:Explorer(LCT裸题 也可用线段树模拟并查集维护连通性)

    题意:给定N,M,然后给出M组信息(u,v,l,r),表示u到v有[l,r]范围的通行证有效.问有多少种通行证可以使得1和N连通. 思路:和bzoj魔法森林有点像,LCT维护最小生成树.  开始和队友 ...

随机推荐

  1. [Agc029E]Wandering TKHS_树形dp_树上差分

    Wandering TKHS 题目链接:https://atcoder.jp/contests/agc029/tasks/agc029_e 数据范围:略. 题解: 好神啊 Orz司队 https:// ...

  2. Servlet的request和response

    SERVLET API中forward() 与redirect()的区别?  答:前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址:后者则是完全的跳转,浏览器将会得到跳转的地址 ...

  3. 剑指offer12:求解double类型的浮点数base和int类型的整数exponent的次方。 保证base和exponent不同时为0

    1. 题目描述 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方.保证base和exponent不同时为0. 2. 思路和方法 分析: 由于 ...

  4. python中int是什么类型

    python中的基本数据类型 1:虽然python中的变量不需要声明,但使用时必须赋值整形变量浮点型变量字符型2:可以一个给多个变量赋值,也可以多个给多个变量赋值3:python3中有6个标准数据类型 ...

  5. js图片压缩上传

    最近公司的移动产品相约app要做一次活动,涉及到图片上传,图片又不能太大,不然用户体验太差,必须先压缩再上传,所以用到了html5的canvas和FileReader,代码先上,小弟前端经验不足,代码 ...

  6. composer在windows下安装并且设置全局变量

    Composer是 PHP 用来管理依赖(dependency)关系的工具.你可以在自己的项目中声明所依赖的外部工具库(libraries),Composer 会帮你安装这些依赖的库文件. 1丶使用安 ...

  7. 第十一章 ZYNQ-MIZ701 PS读写PL端BRAM

      本篇文章目的是使用Block Memory进行PS和PL的数据交互或者数据共享,通过zynq PS端的Master GP0端口向BRAM写数据,然后再通过PS端的Mater GP1把数据读出来,将 ...

  8. 【KMP】OKR-Periods of Words

    [KMP]OKR-Periods of Words 题目描述 串是有限个小写字符的序列,特别的,一个空序列也可以是一个串.一个串P是串A的前缀,当且仅当存在串B,使得A=PB.如果P≠A并且P不是一个 ...

  9. mysql批量修改数据库表引擎

    数据库表之前的引擎是MyISAM,影响事务操作,要改成Innodb引擎 查询表引擎 SELECT CONCAT(table_name,' ', engine) FROM information_sch ...

  10. 5分钟搞定图片鉴黄web应用!

    函数工作流(FunctionGraph,FGS)是一项基于事件驱动的函数托管计算服务,托管函数具备以毫秒级弹性伸缩.免运维.高可靠的方式运行.通过函数工作流,开发者无需配置和管理服务器,只需关注业务逻 ...