记录编号 |
89417 |
评测结果 |
AAAAAAAAAA |
题目名称 |
[AHOI 2005] LANE 航线规划 |
最终得分 |
100 |
用户昵称 |
cstdio |
是否通过 |
通过 |
代码语言 |
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;
}