题解 [AHOI2005] 航线规划
题解 [AHOI2005] 航线规划
很神的一道树剖题。
[AHOI2005] 航线规划
首先对于关键航线有一个很显然的性质:
如果存在一个环,则环上的所有点都不可能是关键航线
注意到题中给定了 无论航线如何被破坏,任意时刻任意两个星球都能够相互到达。在整个数据中,任意两个星球之间最多只可能存在一条直接的航线。,意味着能删的边都删了,就是一棵树了。众所周知,树的用处多的一批,所以我们要是能把这个图转成树就好了。
怎么转呢?直接原图上建生成树,不过这棵树不能包括要删掉的边,为什么可以呢?因为这张图一定是连通的。对于删边操作很难做,所以我们把顺序删边转成逆序加边,结果是一样的。
我们把边权化为点权,一开始所有点的点权都为 \(1\),在这棵树上有两点要加一条边,树上这两点路径上的所有点(因为是边权化点权,所以除 \(lca\))的点权都要变为 \(0\)(对答案无贡献),特殊的,对于图中非树边且不会被删除的边,我们用一次 \(dfs\) 特别处理掉即可。
还有一个细节就是怎么确定删掉了哪一条边,我这里用了 \(map\) 来快速记,如果直接循环找的话应该可以被菊花图卡掉。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int ans=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
return ans*f;
}
const int N=1e5+5;
int n,m,cnt,ans[N];
int hd[N],to[N<<1],nx[N<<1],tot,vis[N<<1];
map<int,int> mp;
int q[N][3];
void adde(int u,int v){
nx[++tot]=hd[u];to[tot]=v;hd[u]=tot;
mp[u*(n+1)+v]=tot;
nx[++tot]=hd[v];to[tot]=u;hd[v]=tot;
mp[v*(n+1)+u]=tot;
}
int sz[N],fa[N],dep[N],son[N],dfn[N],seq[N],top[N],dfstime;
void dfs1(int u,int father){
sz[u]=1,fa[u]=father,dep[u]=dep[father]+1;
for(int i=hd[u];i;i=nx[i]){
int v=to[i];
if(v==father||vis[i]||sz[v]) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[son[u]]<=sz[v]) son[u]=v;
}
}
void dfs2(int u,int anc){
dfn[u]=++dfstime,top[u]=anc;
if(son[u]) dfs2(son[u],anc);
for(int i=hd[u];i;i=nx[i]){
int v=to[i];
if(v!=fa[u]&&v!=son[u]&&!vis[i]&&fa[v]==u) dfs2(v,v);
}
}
#define ls x<<1
#define rs x<<1|1
int sum[N<<2],lazy[N<<2];
void build(int x,int l,int r){
if(l==r){
sum[x]=1;
return;
}
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
sum[x]=sum[ls]+sum[rs];
}
void pushdown(int x,int l,int r){
if(!lazy[x]) return;
sum[ls]=sum[rs]=lazy[x]=0;
lazy[ls]=lazy[rs]=1;
}
void update(int x,int l,int r,int xl,int xr){
if(xl<=l&&xr>=r){
sum[x]=0;
lazy[x]=1;
return;
}
pushdown(x,l,r);
int mid=l+r>>1;
if(xl<=mid) update(ls,l,mid,xl,xr);
if(xr>mid) update(rs,mid+1,r,xl,xr);
sum[x]=sum[ls]+sum[rs];
}
int query(int x,int l,int r,int xl,int xr){
if(xl<=l&&xr>=r) return sum[x];
pushdown(x,l,r);
int mid=l+r>>1,res=0;
if(xl<=mid) res+=query(ls,l,mid,xl,xr);
if(xr>mid) res+=query(rs,mid+1,r,xl,xr);
return res;
}
void modify(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,1,n,dfn[top[u]],dfn[u]);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
update(1,1,n,dfn[u]+1,dfn[v]);
}
int ask(int u,int v){
int res=0;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res+=query(1,1,n,dfn[top[u]],dfn[u]);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
res+=query(1,1,n,dfn[u]+1,dfn[v]);
return res;
}
void dfs3(int u){
for(int i=hd[u];i;i=nx[i]){
int v=to[i];
if(vis[i]) continue;
if(fa[v]==u) dfs3(v);
else if(fa[u]!=v&&dep[v]<dep[u]) modify(u,v);
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int x=read(),y=read();
adde(x,y);
}
while(1){
int x=read();
if(x==-1) break;
q[++cnt][0]=x;
q[cnt][1]=read(),q[cnt][2]=read();
if(!x) vis[mp[q[cnt][1]*(n+1)+q[cnt][2]]]=vis[mp[q[cnt][2]*(n+1)+q[cnt][1]]]=1;
}
dep[1]=1;
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
dfs3(1);
for(int i=cnt;i>=1;i--){
if(q[i][0]) ans[i]=ask(q[i][1],q[i][2]);
else modify(q[i][1],q[i][2]);
}
for(int i=1;i<=cnt;i++){
if(q[i][0]) printf("%d\n",ans[i]);
}
return 0;
}

浙公网安备 33010602011771号