题意:一棵无向连通图,存在重边,q次操作每次加一条边,问每次加边后的割边数。
解析:最朴实的思想是缩点然后对树上不断求lca,虽然可以但是有点麻烦。可以把low数组的含义改变下,不再存dfn_cnt的最小值,而是可以到达的dfn最小点的下标,这样实际上跑一遍tarjan就可以求出一个用并查集维护好的树。这样修改时只需要不断向上合并父节点就可以了,感觉这个思路很巧妙,是对于tarjan思想的一种变形改造。
代码:
#include <cmath> //定义数学函数
#include <cstdio> //定义输入/输出函数
#include <cstdlib> //定义杂项函数及内存分配函数
#include <cstring> //字符串处理
#include <algorithm> //STL 通用算法
#include <cmath>
#include <list> //STL 线性列表容器
#include <map> //STL 映射容器
#include <iostream>
#include <queue> //STL 队列容器
#include <set> //STL 集合容器#
#include <stack> //STL 堆栈容器
#include <string> //字符串类
#include <vector> //STL 动态数组容器
#include <sstream>
#define x first
#define y second
#define mid (l+r>>1)
#define lo (o<<1)
#define ro (o<<1|1)
using namespace std;
typedef long long ll;
typedef vector<int>vi;
typedef pair<int,int>pii;
struct tri{int x,y,z;};
const int inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;
const int N=1e5+10;
const ll mod=1e9+7;
const double PI=acos(0)*2;
int n,m,dfn_cnt,dfn[N],low[N],fa[N],dep[N];
vi edg[N];
int Min(int a,int b)
{
if(dfn[a]<dfn[b])return a;
else return b;
}
void tarjan(int u)
{
dfn[u]=++dfn_cnt;
low[u]=u;
bool flag=0;
for(int i=0;i<edg[u].size();i++)
{
int v=edg[u][i];
if(v==fa[u]&&!flag)
{
flag=1;
continue;
}
if(!dfn[v])
{
dep[v]=dep[u]+1;
fa[v]=u;
tarjan(v);
}
low[u]=Min(low[u],low[v]);
}
}
int ff(int a)
{
return a==low[a]?a:low[a]=ff(low[a]);
}
void solve()
{
for(int i=1;i<=n;i++)edg[i].clear();
memset(dfn+1,0,sizeof(*dfn)*n);
dfn_cnt=0;
while(m--)
{
int a,b;
cin>>a>>b;
edg[a].push_back(b);
edg[b].push_back(a);
}
tarjan(1);
int ans=-1;
for(int i=1;i<=n;i++)if(ff(i)==i)ans++;
int q;cin>>q;
while(q--)
{
int a,b;cin>>a>>b;
a=ff(a),b=ff(b);
while(a!=b)
{
if(dep[a]>dep[b])swap(a,b);
low[b]=fa[b];
b=ff(b);
ans--;
}
cout<<ans<<endl;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
// freopen("in.txt","r",stdin);
for(int cas=1;;cas++)
{
cin>>n>>m;
if(!n&&!m)return 0;
cout<<"Case "<<cas<<":"<<endl;
solve();
cout<<endl;
}
return 0;
}