记录编号 89417 评测结果 AAAAAAAAAA
题目名称 [AHOI 2005] LANE 航线规划 最终得分 100
用户昵称 Gravatarcstdio 是否通过 通过
代码语言 C++ 运行时间 0.555 s
提交时间 2014-03-02 16:00:00 内存使用 9.59 MiB
显示代码纯文本
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<iomanip>
#include<map>
#include<set>
#include<cmath>
#include<deque>
using namespace std;
const int SIZEN=30020,SIZEM=100100;
class TARRAY{//改段求点的树状数组
public:
	int n;
	int s[SIZEN];
	void clear(int x){n=x;memset(s,0,sizeof(s));}
	int lowbit(int x){return x&(-x);}
	void prefchange(int x,int t){
		for(int i=x;i>0;i-=lowbit(i)) s[i]+=t;
	}
	void change(int l,int r,int t){
		prefchange(r,t);
		prefchange(l-1,-t);
	}
	int nodeval(int x){
		int ans=0;
		for(int i=x;i<=n;i+=lowbit(i)) ans+=s[i];
		return ans;
	}
};
class REQUEST{
public:
	int cmd;
	int a,b;
};
vector<REQUEST> req;//请求
deque<int> ans;//答案
vector<pair<int,int> > c[SIZEN];//邻接表,first是点,second是是否为桥(桥1否则0)
int N,M;//N个点M条边
int father[SIZEN]={0};
TARRAY condep;
int dfn[SIZEN]={0},low[SIZEN]={0};
int numpst[SIZEN]={0};
int color[SIZEN]={0};
int ufs[SIZEN]={0};//并查集,维护该点所在的边双中最浅的点
int dfslis[SIZEN*10]={0},dfslen=0;
int first[SIZEN]={0};//在序列中首次出现的位置
int ST[SIZEN*2][30]={0};//这个数组一定要是SIZEN*2......因为是DFS序列的长度
int getdeep(int x){//x的深度
	return condep.nodeval(dfn[x]);
}
void STLCA_prepare(void){
	int m=floor(log(dfslen+0.0)/log(2.0));
	for(int i=1;i<=dfslen;i++) ST[i][0]=dfslis[i];
	for(int j=1;j<=m;j++){
		for(int i=1;i<=dfslen;i++){
			if(i+(1<<j)-1>dfslen) continue;
			int x=ST[i][j-1],y=ST[i+(1<<(j-1))][j-1];
			if(getdeep(x)<getdeep(y)) ST[i][j]=x;
			else ST[i][j]=y;
		}
	}
}
int STLCA(int a,int b){
	int l=first[a],r=first[b];
	if(l>r) swap(l,r);
	int m=floor(log(r-l+1.0)/log(2.0));
	int x=ST[l][m],y=ST[r-(1<<m)+1][m];
	return getdeep(x)<getdeep(y)?x:y;
}
int grand(int x){
	return ufs[x]==x?x:ufs[x]=grand(ufs[x]);
}
int bridgenum(int x,int y){//桥数等于缩点后树上的路径长
	int gx=grand(x),gy=grand(y);
	if(gx==gy) return 0;
	return getdeep(gx)+getdeep(gy)-2*getdeep(grand(STLCA(gx,gy)));
}
void addedge(int x,int y){//加边
	int gx=grand(x),gy=grand(y);
	if(gx==gy) return;
	int anc=grand(STLCA(gx,gy)),ad;//公共祖先
	ad=getdeep(anc);
	vector<int> st;
	for(int now=gx;now!=anc;now=grand(father[now])) st.push_back(now);
	for(int i=st.size()-1;i>=0;i--) condep.change(dfn[st[i]],dfn[st[i]]+numpst[st[i]]-1,ad-getdeep(st[i])),ufs[st[i]]=anc;
	st.clear();
	for(int now=gy;now!=anc;now=grand(father[now])) st.push_back(now);
	for(int i=st.size()-1;i>=0;i--) condep.change(dfn[st[i]],dfn[st[i]]+numpst[st[i]]-1,ad-getdeep(st[i])),ufs[st[i]]=anc;
	//分别把两条链上的点都提上去,因为此前已经将每个边双点的深度置为一样,因此它能正确地修改子树的深度
}
void offlinework(void){//离线处理
	for(int i=req.size()-1;i>=0;i--){
		if(req[i].cmd==1) ans.push_front(bridgenum(req[i].a,req[i].b));
		else if(req[i].cmd==0) addedge(req[i].a,req[i].b);
	}
}
void markDCC(int x,int sst,int dep){//对双连通分量进行标号,当前DCC中最浅的点是sst
	if(color[x]) return;
	color[x]=1;
	ufs[x]=sst;
	condep.change(dfn[x],dfn[x],dep);
	dfslis[++dfslen]=x;first[x]=dfslen;
	for(int i=0;i<c[x].size();i++){
		if(color[c[x][i].first]) continue;
		if(c[x][i].second) markDCC(c[x][i].first,c[x][i].first,dep+1);
		else markDCC(c[x][i].first,sst,dep);
		dfslis[++dfslen]=x;
	}
}
int timer=0;
void Tarjan(int x){
	color[x]=-1;
	dfn[x]=low[x]=++timer;
	numpst[x]=1;
	for(int i=0;i<c[x].size();i++){
		int v=c[x][i].first;
		if(color[v]==0){
			father[v]=x;
			Tarjan(v);
			numpst[x]+=numpst[v];
			low[x]=min(low[v],low[x]);
		}
		else if(color[v]==-1&&v!=father[x]) low[x]=min(low[x],dfn[v]);
		if(dfn[x]<low[v]) c[x][i].second=1;//标记为桥
	}
}
set<pair<int,int> > edges;
void init(void){
	scanf("%d%d",&N,&M);
	condep.clear(N);
	int u,v;
	pair<int,int> pr;
	for(int i=1;i<=M;i++){
		scanf("%d%d",&u,&v);
		if(u>v) swap(u,v);
		pr=make_pair(u,v);
		edges.insert(pr);
	}
	REQUEST nc;
	while(true){
		scanf("%d",&nc.cmd);
		if(nc.cmd==-1) break;
		scanf("%d%d",&nc.a,&nc.b);
		if(nc.a>nc.b) swap(nc.a,nc.b);
		req.push_back(nc);
		if(nc.cmd==0){//破坏一条边
			pr=make_pair(nc.a,nc.b);
			edges.erase(edges.find(pr));
		}
	}
	set<pair<int,int> >::iterator key;
	for(key=edges.begin();key!=edges.end();key++){
		u=key->first,v=key->second;
		c[u].push_back(make_pair(v,0));
		c[v].push_back(make_pair(u,0));
	}
}
int main(){
	freopen("lane.in","r",stdin);
	freopen("lane.out","w",stdout);
	init();//此时,c中存的就是所有询问之后的边信息
	Tarjan(1);
	memset(color,0,sizeof(color));
	markDCC(1,1,0);
	STLCA_prepare();
	offlinework();
	for(int i=0;i<ans.size();i++) printf("%d\n",ans[i]);
	return 0;
}