[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]); }