【动态缩点】【bzoj 1969】: [Ahoi2005]LANE 航线规划

http://www.lydsy.com/JudgeOnline/problem.php?id=1969


一道十分SXBK的题,涉及到双连通、LCA、并查集、离线思想、用BIT维护dfs序和并查集维护LCA的动态缩点,看看蒟蒻用了4个namespace就知道有多BT了


首先如果这道题没有删除操作了话就十分的水了,直接双连通+LCA

但是出题人偏偏要加个删除,变化得就远远不止像bzoj1015了


有了删除,第一个反应是离线,倒过来就需要动态缩点了

每次缩点是将一个环缩为一个点,这样看起来很麻烦,其实不然

只需要缩两个点到LCA的两条链即可


我维护了两颗树,一颗是最初缩点得到的树,一个是正在缩点的树

我发现了一种缩点的新方法——并查集

最开始双连通用并查集来缩点,得到了原始树,然后动态缩点的过程中只变化了缩点树

这么做当然是有原因的


因为我用的是倍增算法,所以动态树上两点维护距离的时候需要动态维护LCA和dep[]

经过观察可得,两点之间的LCA无论怎么缩点都是不变滴(前提是向LCA的方向缩点)

但是dep[]是不断改变的,怎么变化的捏?

每一次缩点都会将子树所有dep都-1

所以会考虑到用BIT维护dfs序


注意每次原始树的节点u缩对应缩点树的节点是find(u),所以每次都要find一下,我算lca的时候就忘记find了。。。

然后狂艹代码。。。


//#define _TEST _TEST
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
/************************************************
Code By willinglive    Blog:http://willinglive.cf
************************************************/
#define rep(i,l,r) for(int i=(l),___t=(r);i<=___t;i++)
#define per(i,r,l) for(int i=(r),___t=(l);i>=___t;i--)
#define MS(arr,x) memset(arr,x,sizeof(arr))
#define LL long long
#define INE(i,u,e) for(int i=head[u];~i;i=e[i].next)
#define MP make_pair
typedef pair<int,int>pii;
inline const int read()
{int r=0,k=1;char c=getchar();for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';return k*r;}
/
const int N=30010;
const int M=100010;
int n,m;
int out[N],in[N],top;
struct Option{int c,a,b;}opt[40010]; int all;
/
inline void MIN(int &a,int b){if(a>b)a=b;}
namespace UFS
{///
int fa[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void unio(int x,int y){fa[find(y)]=find(x);}
}///
namespace BIT
{///
int c[N*2];
int query(int o){int s=0;for(;o<=n<<1;o+=o&-o)s+=c[o];return s;}
void add(int o,int x){for(;o;o-=o&-o)c[o]+=x;}
}///
namespace Graph
{///
struct Edge{int v,next; bool del;}e[2*M];
int head[N],k;
map<pii,bool>edge;
void adde(int u,int v){e[k].v=v;e[k].next=head[u];head[u]=k++;}
int dfn[N],low[N],dfsclock;
int fa[N];
void buildgraph()
{
	for(map<pii,bool>::iterator it=edge.begin();it!=edge.end();it++)
	    if(it->second)
	    {
	    	int u=it->first.first,v=it->first.second;
	    	adde(u,v); adde(v,u);
	    }
}
void tarjan(int u,int fa)
{
	dfn[u]=low[u]=++dfsclock;
	INE(i,u,e)
	{
		int v=e[i].v; if(v==fa) continue;
		if(!dfn[v])
		{
			tarjan(v,u);
			MIN(low[u],low[v]);
			if(low[v]<=dfn[u]) UFS::unio(u,v);
		}
		else MIN(low[u],dfn[v]);
	}
}
void contract(int x,int y)
{
	int f;
	for(;x!=y;x=f)
	{
		BIT::add(out[x],1); BIT::add(in[x]-1,-1);
		f=UFS::find(fa[x]);
		fa[x]=fa[y];
		UFS::unio(y,x);
	}
}
}///
namespace Tree
{///
struct Edge{int v,next; bool del;}e[2*M];
int head[N],k;
void adde(int u,int v){e[k].v=v;e[k].next=head[u];head[u]=k++;}
int dep[N];
int fa[N][16];
void buildtree()
{
	static bool vis[N]={0};
	rep(u,1,n)
	{
		int fu=UFS::find(u); if(vis[fu]) continue; vis[fu]=1;
		for(int i=Graph::head[u];~i;i=Graph::e[i].next)
		{
			int fv=UFS::find(Graph::e[i].v); if(fu==fv) continue;
			adde(fu,fv); adde(fv,fu);
		}
	}
}
void copytree()
{
	memcpy(Graph::e,e,sizeof(e));
	memcpy(Graph::head,head,sizeof(head));
	Graph::k=k;
	rep(i,1,n)
	{
		int rt=UFS::find(i);
		Graph::fa[i]=fa[rt][0];
	}
}
void dfs(int u,int d)
{
	in[u]=++top;
	dep[u]=d;
	INE(i,u,e)
	{
		int v=e[i].v; if(dep[v]) continue;
		fa[v][0]=u; rep(i,1,15) fa[v][i]=fa[fa[v][i-1]][i-1];
		dfs(v,d+1);
	}
	out[u]=++top;
}
int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	int d=dep[x]-dep[y];
	rep(i,0,15) if(d&(1<<i)) x=fa[x][i];
	if(x==y) return x;
	per(i,15,0) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
int getdep(int u)
{
	return dep[u]-BIT::query(in[u]);
}
}///
/
void input()
{
	MS(Graph::head,-1); MS(Tree::head,-1);
	using namespace Graph;
    n=read(); m=read();
    int u,v;
    rep(i,1,m)
    {
    	u=read(); v=read();
    	if(u>v) swap(u,v);
    	edge[MP(u,v)]=1;
    }
    int c,a,b;
    while(c=read(),c!=-1)
    {
    	a=read(); b=read();
    	if(a>b) swap(a,b);
    	all++; opt[all].c=c; opt[all].a=a; opt[all].b=b;
    	if(!c) edge[MP(a,b)]=0;
    }
}
void solve()
{
  	rep(i,1,n) UFS::fa[i]=i;
	using namespace Graph;
	buildgraph();
    tarjan(1,-1);
    using namespace Tree;
    buildtree();
    dfs(1,1);
    copytree();
  	using namespace UFS;
  	static int ans[40010];
  	per(i,all,1)
  	{
  		int a=find(opt[i].a),b=find(opt[i].b);
  		int x=find(lca(a,b));
  		if(opt[i].c==0)
  		{
  			contract(a,x);
  			contract(b,x);
  		}
  		else
  		{
  			ans[++*ans]=getdep(a)+getdep(b)-2*getdep(x);
  		}
  	}
  	per(i,*ans,1) printf("%d\n",ans[i]);
}
/
int main()
{
    #ifndef _TEST
    freopen("lane.in","r",stdin); freopen("std.out","w",stdout);
    #endif
    input(),solve();
    return 0;
}


UPD Jan.31

听说可以直接链剖,貌似是这样滴,23333



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值