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

    GDB 一.gdb常用命令: 命令 描述 backtrace(或bt) 查看各级函数调用及参数 finish 连续运行到当前函数返回为止,然后停下来等待命令 frame(或f) 帧编号 选择栈帧 in ...

  2. [Agc029C]Lexicographic constraints_进制_二分答案_贪心

    Lexicographic constraints 题目链接:https://atcoder.jp/contests/agc029/tasks/agc029_c 数据范围:略. 题解: 二分是显然的, ...

  3. windows 修改Administrator管理员账户名

      用[Win+R]组合键命令打开[运行]界面,输入[gpedit.msc],按[回车键]或[鼠标左键]单击[确定]按钮:   在弹出的[本地组策略编辑器]对话框中,依次[鼠标左键]点击打开:[计算机 ...

  4. python 修改文件的创建时间、修改时间、访问时间

    目录 python 修改文件创建.修改.访问时间 方案一 方案二(无法修改文件创建时间) python 修改文件创建.修改.访问时间 突如其来想知道一下 python 如何修改文件的属性(创建.修改. ...

  5. ThreadLocal使用场景,原理

    ThreadLocal 1. 先说下 ThreadLocal不能解决多线程间共享数据,他是一个隔离多线程间共享数据的好帮手 2. ThreadLocal是本地线程共享数据 3. 他是以空间换时间 sy ...

  6. JSONObject,JSONArray,对象,数组互相转化

    json类型对象转化成对象类型 JSONObject.toJavaObject(jsonObj, Object.class) json类型对象转化为List类型 JSONArray.parseArra ...

  7. Vasya's Function CodeForces - 837E (gcd)

    大意: 给定$a,b$, $1\le a,b\le 1e12$, 定义 $f(a,0)=0$ $f(a,b)=1+f(a,b-gcd(a,b))$ 求$f(a,b)$. 观察可以发现, 每次$b$一定 ...

  8. python 识别图像主题并切割

    两种办法,一种是用百度的API,效果还可以,不过好像每天有50次的调用的限制 from aip import AipImageClassify import cv2 """ ...

  9. c# 图文添加文字斜水印 优化

    之前一篇给图片加水印的功能,加出来水印的图片位置有一点问题,并且如果图片分辨率有变动的话,水印会有层次不齐的问题. 目前只能优化到增加一条居中显示的斜水印,在不同分辨率不同大小的图片中,都能保证文字水 ...

  10. 钉钉微应用内置浏览器js缓存清理

    html中引用的js文件加上版本号,如下: <script type="text/javascript" src="js/xxx.js?version=1.0.1& ...