Loading

题解 [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;
}
posted @ 2021-02-19 10:06  Quick_Kk  阅读(64)  评论(2编辑  收藏  举报