HDU 5458 Stability(树链剖分缩点并查集)

思路:有两种操作,一种是删除一条边,另一种是询问点u,v之间的桥的数目,显然删边是不好操作的,一个经典的套路就是离线把操作存起来,从最后的图开始加边,因为题目保证最后的图一定是联通的,所以不用考虑别的,当它最后的图是一棵树的时候,我们设边权为1,那么u,v之间的桥的数目就是两点间的距离,而如果加一条边的话那么u,v会形成环,那么就将它们的边权设为0,对答案没有贡献,那么就可以用并查集将最后的图缩成一棵树,然后树链剖分即可


#include<bits/stdc++.h>
#pragma comment(linker, "/STACK:102400000,102400000")  
using namespace std;
const int maxn = 3*1e5+7;
const int maxq = 1e5+7;
#define LL long long
int siz[maxn],fa[maxn],son[maxn],dep[maxn],top[maxn],id[maxn];
int tot;
vector<int>e[maxn];
map<int,int>tmp[maxn];
map<int,int>::iterator it;
//vector<pair<int,int> >tmp;
//vector<pair<int,int> >::iterator it;
int n,m,k;
int sum[maxn<<2],lazy[maxn<<2];
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
struct Node
{
	int op;
	int u,v;
	int ans;
}q[maxq];
void push_up(int i)
{
	sum[i]=sum[i<<1]+sum[i<<1|1];
}
void build(int i,int l,int r)
{
	lazy[i]=0;
	if(l==r)
	{
		sum[i]=1;
		return;
	}
	int mid = (l+r)>>1;
	build(lson);
	build(rson);
	push_up(i);
}

int query(int ql,int qr,int i,int l,int r)
{
	if(lazy[i])return 0;
	if(ql<=l && qr>=r)
		return sum[i];
	int mid = (l+r)>>1;
	int ans = 0;
	if(ql<=mid)
		ans+=query(ql,qr,lson);
	if(mid<qr)
		ans+=query(ql,qr,rson);
	return ans;
}
void update(int ql,int qr,int i,int l,int r)
{
	if(lazy[i])return;
	if(ql<=l&&qr>=r)
	{
		lazy[i]=1;
		sum[i]=0;
		return;
	}
	int mid = (l+r)>>1;
	if(ql<=mid)
		update(ql,qr,lson);
	if(qr>mid)
		update(ql,qr,rson);
	push_up(i);
}
void dfs1(int u,int f,int d)
{
	siz[u]=1;
	son[u]=0;
	fa[u]=f;
	dep[u]=d;
	for(int i = 0;i<e[u].size();i++)
	{
		int v = e[u][i];
		if(v==f)continue;
		dfs1(v,u,d+1);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v])
			son[u]=v;
	}
}
void dfs2(int u,int tp)
{
	top[u]=tp;
	id[u]=++tot;
	if(son[u])
		dfs2(son[u],tp);
	for(int i = 0;i<e[u].size();i++)
	{
		int v = e[u][i];
		if(v==fa[u] || v==son[u])
			continue;
		dfs2(v,v);
	}
}
void sccno(int u,int v)
{
	int tp1 = top[u],tp2 = top[v];
	while(tp1!=tp2)
	{
		if(dep[tp1]<dep[tp2])
		{
			swap(tp1,tp2);
			swap(u,v);
		}
		update(id[tp1],id[u],1,1,tot);
		u = fa[tp1];
		tp1 = top[u];
	}
	if(u==v)
		return;
	if(dep[u]>dep[v])
		swap(u,v);
	update(id[son[u]],id[v],1,1,tot);
}
LL Yougth(int u,int v)
{
	int tp1 = top[u],tp2 = top[v];
	LL ans = 0;
	while(tp1!=tp2)
	{
		if(dep[tp1]<dep[tp2])
		{
			swap(tp1,tp2);
			swap(u,v);
		}
		ans+=query(id[tp1],id[u],1,1,tot);
		u = fa[tp1];
		tp1 = top[u];
	}
	if(u==v)
		return ans;
	if(dep[u]>dep[v])
		swap(u,v);
	ans+=query(id[son[u]],id[v],1,1,tot);
	return ans;
}
int p[maxn];
int Find(int x){return p[x]==x?x:p[x]=Find(p[x]);}
void init()
{
	for(int i = 0;i<=n;i++)
		p[i]=i;
	for(int i = 0;i<=n;i++)
		tmp[i].clear();
	for(int i = 0;i<=n;i++)
		e[i].clear();
}
int main()
{
   int T,cas=1;
   scanf("%d",&T);
   while(T--)
   {
	   printf("Case #%d:\n",cas++);
       scanf("%d%d%d",&n,&m,&k);
	   init();
	   for(int i = 1;i<=m;i++)
	   {
		   int u,v;
		   scanf("%d%d",&u,&v);
		   if(u>v)swap(u,v);
		   tmp[u][v]++;
		   //tmp.push_back(make_pair(u,v));
	   }
	   for(int i = 1;i<=k;i++)
	   {
           scanf("%d%d%d",&q[i].op,&q[i].u,&q[i].v);
		   if(q[i].u>q[i].v)
			   swap(q[i].u,q[i].v);
		  /* if(q[i].op==1)
		   {
               it = find(tmp.begin(),tmp.end(),make_pair(q[i].u,q[i].v));
			   tmp.erase(it);
			   printf("yes\n");
		   }*/
		   if(q[i].op==1)
			   tmp[q[i].u][q[i].v]--;
	   }
       /*for(int i = 0;i<tmp.size();i++)
	   {
		    int u = tmp[i].first;
			int v = tmp[i].second;
            if(Find(u)!=Find(v))
			{
				p[Find(u)]=Find(v);
				e[u].push_back(v);
				e[v].push_back(u);
				it = find(tmp.begin(),tmp.end(),make_pair(u,v));
				tmp.erase(it);
			}
	   }*/
	   for(int i = 1;i<=n;i++)
	   {
		   for(it = tmp[i].begin();it!=tmp[i].end();it++)
		   {
			   if(it->second > 0)
			   {
				   int u = i;
				   int v = it->first;
				   if(Find(u)!=Find(v))
				   {
					   e[u].push_back(v);
					   e[v].push_back(u);
					   p[Find(u)]=Find(v);
					   it->second--;
				   }
			   }
		   }
	   }
	  // printf("yes\n");
	   /*for(int i = 1;i<=n;i++)
	   {
		   for(int j = 0;j<e[i].size();j++)
		   {
			   printf("%d %d\n",i,e[i][j]);
		   }
	   }*/
       dfs1(1,-1,0);
	   dfs2(1,1);
	   build(1,1,tot);
	   for(int i = 1;i<=n;i++)
	   {
		   for(it = tmp[i].begin();it!=tmp[i].end();it++)
		   {
			   if(it->second>0)
			   {
				   int u = i,v = it->first;
				   sccno(u,v);
			   }
		   }
	   }
	   /*for(int i = 0;i<tmp.size();i++)
	   {
		   int u = tmp[i].first;
		   int v = tmp[i].second;
		   sccno(u,v);
	   }*/
       for(int i = k;i>=1;i--)
	   {
		   if(q[i].op==1)
			   sccno(q[i].u,q[i].v);
		   else
			   q[i].ans = Yougth(q[i].u,q[i].v);
	   }
	   for(int i = 1;i<=k;i++)
		   if(q[i].op==2)
			   printf("%d\n",q[i].ans);
   }
}

Description

Given an undirected connected graph   with   nodes and   edges, with possibly repeated edges and/or loops. The stability of connectedness between node   and node   is defined by the number of edges in this graph which determines the connectedness between them (once we delete this edge, node   and   would be disconnected). 

You need to maintain the graph  , support the deletions of edges (though we guarantee the graph would always be connected), and answer the query of stability for two given nodes.

Input

There are multiple test cases(no more than   cases), and the first line contains an integer  , meaning the totally number of test cases. 

For each test case, the first line contains three integers   and  , where   and  . The nodes in graph   are labelled from   to 

Each of the following   lines contains two integers   and   describing an undirected edge between node   and node 

Following   lines - each line describes an operation or a query in the formats: 
  : delete one edge between   and  . We guarantee the existence of such edge. 
  : query the stability between   and 

Output

For each test case, you should print first the identifier of the test case. 

Then for each query, print one line containing the stability between corresponding pair of nodes.

Sample Input

1
10 12 14
1 2
1 3
2 4
2 5
3 6
4 7
4 8
5 8
6 10
7 9
8 9
8 10
2 7 9
2 7 10
2 10 6
2 10 5
1 10 6
2 10 1
2 10 6
2 3 10
1 8 5
2 5 10
2 4 5
1 7 9
2 7 9
2 10 5

Sample Output

Case #1:
0
0
0
0
2
4
3
3
2
3
4


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值