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

    在linux编程中,有时候会用到定时功能,常见的是用sleep(time)函数来睡眠time秒:但是这个函数是可以被中断的,也就是说当进程在睡眠的过程中,如果被中断,那么当中断结束回来再执行该进程的时 ...

  2. redis的安装和连接

  3. loadrunner如何监控linux,以及重点指标分析

    监控UNIX一. lr监控UNIX ,UNIX先启动一rstatd服务 以下是在IBM AIX系统中启动rstatd服务的方法: 1. 使用telnet以root用户的身份登录入AIX系统 2. 在命 ...

  4. Lucene 全文检索

    基于 lucene 8 1 Lucene简介 Lucene是apache下的一个开源的全文检索引擎工具包. 1.1 全文检索(Full-text Search) 全文检索就是先分词创建索引,再执行搜索 ...

  5. EffectiveC++

    宁可以编译器替换预处理器 define 不被视为语言的一部分,因此也就有可能在预处理阶段被优化掉,导致相关变量出现错误. #define ASPECT_RATIO 1.63 //可以尝试将其替换为 c ...

  6. Angular 表单验证类库 ngx-validator 1.0 正式发布

    背景介绍 之前写了一篇 <如何优雅的使用 Angular 表单验证>,结尾处介绍了统一验证反馈的类库  ngx-validator  ,由于这段时间一直在新模块做微前端以及相关业务组件库, ...

  7. C语言两个特别大的整数类型相加超出范围使用两个技巧

    技巧1:用long (%ld)或者long long(%lld)类型存取 技巧2:当两个同号的数字相加,放到等号的另一边,变成减号 问题: 给定区间[-2的31次方, 2的31次方]内的3个整数A.B ...

  8. Redis 的订阅发布(PUB/SUB)示例

    ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("127.0.0.1"); ISubscriber sub ...

  9. virtual和override

    偶然间看到的题,借此记录. class Program { static void Main(string[] args) { D d = new D(); //第一个D是申明类,第二个D是实例类 A ...

  10. [转载]PyTorch中permute的用法

    [转载]PyTorch中permute的用法 来源:https://blog.csdn.net/york1996/article/details/81876886 permute(dims) 将ten ...