[AHOI2005]航线规划

题意

给一个n个点m条边的图,有两种操作:询问x到y的路径必经的边有几条,删除x,y之间的直接连边。

1< N < 30000,1 < M < 100000,操作总数不超过40000

我们保证无论航线如何被破坏,任意时刻任意两个星球都能够相互到达。在整个数据中,任意两个星球之间最多只可能存在一条直接的航线。

题解

必经边就是桥(删去x,y不连通那种),但是不好维护。因为无论什么时候两点连通,所以考虑特殊情况:一棵树,树边都是必经边,考虑加边会带来什么影响,会导致树上x到y的路径都不是必经边,当然加进来的边也不是。所以就很好维护了,考虑时光倒流,先用没摧毁的边建一棵树,加边就是路径修改,查询就是路径查询,用树剖就可以了。那些没摧毁的非树边就按照路径修改加进来就可以了。

找摧毁的是哪条边有点恶心(也有可能我写的方法不对),看见有用hash的。

还有就是因为是路径,所以把值赋给儿子节点的话,查询和修改时最后应该是id[x]+1

 

#include<bits/stdc++.h>
using namespace std;

const int maxn=40005;
const int maxm=100005;
int n,m,cnt,num;
int fa[maxn];
bool broken[maxm];//broken 1:没用到的没炸的边
int dep[maxn],size[maxn],son[maxn],id[maxn],top[maxn];
int root,ls[maxn<<1],rs[maxn<<1],sum[maxn<<1];
int tot,ans[maxn];
vector<int>cx[maxn];
struct edge{
  int x,y;
}e[maxm];
struct question{
  int op,x,y,id;
}q[maxn];

template<class T>inline void read(T &x){
  x=0;int f=0;char ch=getchar();
  while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
  while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
  x= f ? -x : x ;
}

bool cmp(edge a,edge b){
  if(a.x==b.x) return a.y<b.y;
  return a.x<b.x;
}

bool cmp1(question a,question b){
  if(a.op!=b.op) return a.op<b.op;
  if(a.x==b.x) return a.y<b.y;
  return a.x<b.x;
}

bool cmp2(question a,question b){
  return a.id>b.id;
}

int find(int x){
  return fa[x]==x ? x : fa[x]=find(fa[x]);
}

void connect(int x,int y,int i){
  int dx=find(x),dy=find(y);
  if(dx!=dy){
    cx[x].push_back(y);
    cx[y].push_back(x);
    fa[dx]=dy;
  }
  else broken[i]=true;
}

void dfs(int u){
  size[u]=1;
  for(unsigned int i=0;i<cx[u].size();i++){
    int v=cx[u][i];
    if(v==fa[u]) continue;
    fa[v]=u;
    dep[v]=dep[u]+1;
    dfs(v);
    size[u]+=size[v];
    if(size[son[u]]<size[v]) son[u]=v;
  }
}

void dfs(int u,int tp){
  id[u]=++num;
  top[u]=tp;
  if(!son[u]) return ;
  dfs(son[u],tp);
  for(unsigned int i=0;i<cx[u].size();i++){
    int v=cx[u][i];
    if(v==fa[u]||v==son[u]) continue;
    dfs(v,v);
  }
}

void update(int rt){
  sum[rt]=sum[ls[rt]]+sum[rs[rt]];
}

void build(int &rt,int l,int r){
  rt=++num;
  if(l==r){
    sum[rt]=1;
    return ;
  }
  int mid=(l+r)>>1;
  build(ls[rt],l,mid);
  build(rs[rt],mid+1,r);
  update(rt);
}

void modify(int rt,int l,int r,int a_l,int a_r){
  if(!sum[rt]) return ;
  if(a_l<=l&&r<=a_r){
    sum[rt]=0;
    return ;
  }
  int mid=(l+r)>>1;
  if(a_l<=mid) modify(ls[rt],l,mid,a_l,a_r);
  if(mid<a_r) modify(rs[rt],mid+1,r,a_l,a_r);
  update(rt);
}

void modify(int x,int y){
  while(top[x]!=top[y]){
    if(dep[top[x]]>dep[top[y]]) swap(x,y);
    modify(1,1,n,id[top[y]],id[y]);
    y=fa[top[y]];
  }
  if(dep[x]>dep[y]) swap(x,y);
  modify(1,1,n,id[x]+1,id[y]);
}

int query(int rt,int l,int r,int a_l,int a_r){
  if(!sum[rt]) return sum[rt];
  if(a_l<=l&&r<=a_r) return sum[rt];
  int mid=(l+r)>>1;
  int ret=0;
  if(a_l<=mid) ret+=query(ls[rt],l,mid,a_l,a_r);
  if(mid<a_r) ret+=query(rs[rt],mid+1,r,a_l,a_r);
  return ret;
}

int query(int x,int y){
  int ret=0;
  while(top[x]!=top[y]){
    if(dep[top[x]]>dep[top[y]]) swap(x,y);
    ret+=query(1,1,n,id[top[y]],id[y]);
    y=fa[top[y]];
  }
  if(dep[x]>dep[y]) swap(x,y);
  ret+=query(1,1,n,id[x]+1,id[y]);
  return ret;
}

int main(){
  read(n);read(m);
  for(int i=1;i<=n;i++) fa[i]=i;
  for(int i=1;i<=m;i++){
    read(e[i].x);read(e[i].y);
    if(e[i].x>e[i].y) swap(e[i].x,e[i].y);
  }
  while(1){
    read(q[++cnt].op);
    if(q[cnt].op==-1) {cnt--;break;}
    read(q[cnt].x);
    read(q[cnt].y);
    q[cnt].id=cnt;
    if(q[cnt].x>q[cnt].y) swap(q[cnt].x,q[cnt].y);
  }
  sort(e+1,e+m+1,cmp);
  sort(q+1,q+cnt+1,cmp1);
  for(int i=1,j=1;;j++){
    while(j<=cnt&&!q[j].op&&i<=m&&(q[j].x!=e[i].x||q[j].y!=e[i].y))
      connect(e[i].x,e[i].y,i),i++;
    if(i>m) break;
    if(j>cnt||q[j].op) connect(e[i].x,e[i].y,i);
    i++;
  }
  memset(fa,0,sizeof(fa));
  dep[1]=1;
  dfs(1);
  dfs(1,1);
  num=0;
  build(root,1,n);
  for(int i=1;i<=m;i++)
   if(broken[i]) modify(e[i].x,e[i].y);
  sort(q+1,q+cnt+1,cmp2);
  for(int i=1;i<=cnt;i++){
    if(!q[i].op) modify(q[i].x,q[i].y);
    else ans[++tot]=query(q[i].x,q[i].y);
  }
  for(int i=tot;i;i--) printf("%d\n",ans[i]);
}
View Code

 

posted @ 2019-08-13 12:14  _JSQ  阅读(187)  评论(0编辑  收藏  举报