ahoi2005 lane 航线规划
来源:互联网 发布:unity3d 镜面反射 编辑:程序博客网 时间:2024/06/11 09:55
题目链接
题解
对于这样一类删边+询问的题目,可以很快就想到“正难则反”这一思路,因此可以离线做,先将要删的边删掉,再反着处理每一个询问,于是删边就被处理成了加边。
然而,即使这样,仍然很麻烦,因为这是一个图,可能有环,非常麻烦。
可以考虑运用dfs树,先利用dfs造出一棵树,然后再将没包含在树上的边加入更新。
对于关键路径,非常显然,就是树上的边了。
现在考虑如何更新。
每加入一条边,就在树上形成了一个环,将这个环上的边全缩成一个点,就可以了,对于缩点,很快就可以想到利用并查集。
询问时,不断利用并查集向上跳,一直到LCA,这样虽然不是正解,但是已经可以过掉这题了。
#include<cstdio>#include<cctype>#include<vector>#include<algorithm>using namespace std;const int M=30005;vector<int>edge[M];int n,m,q,a[40005],b[40005],c[40005];bool mark[M];int fa[M],dep[M];void rec(int x){ mark[x]=1; for(int i=0;i<edge[x].size();i++){ if(mark[edge[x][i]]) continue; fa[edge[x][i]]=x; dep[edge[x][i]]=dep[x]+1; rec(edge[x][i]); }}int par[M],ans[40005];int get(int x){ if(par[x]!=x) par[x]=get(par[x]); return par[x];}int Query(int a,int b){ int ans=0; a=get(a);b=get(b); while(a!=b){ if(dep[a]<dep[b]) swap(a,b); a=get(fa[a]); ans++; } return ans;}void update(int a,int b){ a=get(a); b=get(b); while(a!=b){ if(dep[a]<dep[b]) swap(a,b); par[a]=fa[a]; a=get(a); }}int main(){ scanf("%d%d",&n,&m); for(int i=0;i<m;i++){ int a,b; scanf("%d%d",&a,&b); edge[a].push_back(b); edge[b].push_back(a); } for(int i=1;i<=n;i++) par[i]=i; for(int i=1;i<=n;i++) sort(edge[i].begin(),edge[i].end()); while(true){ scanf("%d",&a[q]); if(a[q]==-1) break; scanf("%d%d",&b[q],&c[q]); if(a[q]==0){ edge[b[q]].erase(lower_bound(edge[b[q]].begin(),edge[b[q]].end(),c[q])); edge[c[q]].erase(lower_bound(edge[c[q]].begin(),edge[c[q]].end(),b[q])); } q++; } rec(1); fa[1]=1; for(int i=1;i<=n;i++){ for(int j=0;j<edge[i].size();j++){ if(fa[i]==edge[i][j]||fa[edge[i][j]]==i) continue; update(edge[i][j],i); } } for(int i=q-1;i>=0;i--){ if(a[i]==0){ update(b[i],c[i]); }else{ ans[i]=Query(b[i],c[i]); } } for(int i=0;i<q;i++) if(a[i]) printf("%d\n",ans[i]); return 0;}
正解还是非常机智的。
考虑一棵树,如果把一条树边删掉,那么它的子树深度就会全部减少一。根据两个点的深度以及两个点的LCA就可以求出这两个点之间的树边的条数。
首先利用dfs序将问题转换到序列上,因为在dfs序中,子树是一段连续的区间。
于是可以利用一个树状数组来维护,这棵树状数组是区间更新,单点询问的。
其余的和上面是一样的。
#include<cstdio>#include<cctype>#include<vector>#include<algorithm>#define lowbit(x) (x&-x)using namespace std;const int M=30005;vector<int>edge[M];int n,m,q,a[40005],b[40005],c[40005];bool mark[M];int fa[M][20],dep[M],dfn[M],dfc,mx[M];void rec(int x){ mark[x]=1; dfn[x]=++dfc; for(int i=0;i<edge[x].size();i++){ if(mark[edge[x][i]]) continue; fa[edge[x][i]][0]=x; dep[edge[x][i]]=dep[x]+1; rec(edge[x][i]); } mx[x]=dfc;}int par[M],ans[40005];int get(int x){ if(par[x]!=x) par[x]=get(par[x]); return par[x];}int bit[M];void add(int x,int w){ for(int i=x;i>0;i-=lowbit(i)) bit[i]+=w;}int sum(int x){ int res=0; for(int i=x;i<=dfc;i+=lowbit(i)) res+=bit[i]; return res;}int up(int a,int step){ for(int i=0;i<20;i++){ if(step&(1<<i)) a=fa[a][i]; } return a;}int LCA(int a,int b){ if(dep[a]<dep[b]) swap(a,b); a=up(a,dep[a]-dep[b]); if(a!=b){ for(int i=19;i>=0;i--){ if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i]; } a=fa[a][0]; } return a;}int Query(int a,int b){ return sum(dfn[a])+sum(dfn[b])-sum(dfn[LCA(a,b)])*2;}void del(int a,int rt){ a=get(a); while(dep[a]>dep[rt]){ add(dfn[a]-1,1); add(mx[a],-1); par[a]=fa[a][0]; a=get(a); }}void update(int a,int b){ int lca=get(LCA(a,b)); del(a,lca); del(b,lca); }inline void Rd(int&res){ res=0;char c; while(c=getchar(),!isdigit(c)); do res=(res<<1)+(res<<3)+(c^48); while(c=getchar(),isdigit(c));}int main(){ scanf("%d%d%d",&n,&m,&q); for(int i=0;i<m;i++){ int a,b; scanf("%d%d",&a,&b); edge[a].push_back(b); edge[b].push_back(a); } for(int i=1;i<=n;i++) par[i]=i; for(int i=1;i<=n;i++) sort(edge[i].begin(),edge[i].end()); for(int i=0;i<q;i++){ Rd(a[i]);Rd(b[i]);Rd(c[i]); if(a[i]==0){ edge[b[i]].erase(lower_bound(edge[b[i]].begin(),edge[b[i]].end(),c[i])); edge[c[i]].erase(lower_bound(edge[c[i]].begin(),edge[c[i]].end(),b[i])); } } rec(1); for(int i=1;i<=n;i++){ add(dfn[i],dep[i]); add(dfn[i]-1,-dep[i]); } for(int j=1;j<20;j++) for(int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; for(int i=1;i<=n;i++){ for(int j=0;j<edge[i].size();j++){ if(fa[i][0]==edge[i][j]||fa[edge[i][j]][0]==i) continue; update(edge[i][j],i); } } for(int i=q-1;i>=0;i--){ if(a[i]==0){ update(b[i],c[i]); }else{ ans[i]=Query(b[i],c[i]); } } for(int i=0;i<q;i++) if(a[i]) printf("%d\n",ans[i]); return 0;}
1 3
- ahoi2005 lane 航线规划
- 【AHOI2005】【BZOJ1969】LANE 航线规划
- [bzoj1969][AHOI2005]LANE 航线规划
- bzoj1969: [Ahoi2005]LANE 航线规划
- bzoj 1969: [Ahoi2005]LANE 航线规划
- [BZOJ1969][Ahoi2005]LANE 航线规划(树链剖分)
- BZOJ 1969: [Ahoi2005]LANE 航线规划
- BZOJ 线段树 1969: [Ahoi2005]LANE 航线规划
- 【动态缩点】【bzoj 1969】: [Ahoi2005]LANE 航线规划
- [BZOJ1969][Ahoi2005]LANE 航线规划(树链剖分+并查集)
- [BZOJ]1969: [Ahoi2005]LANE 航线规划 离线树链剖分
- bzoj 1969: [Ahoi2005]LANE 航线规划 离线+树链剖分+线段树
- ahoi2005 lane 航线规划 (60分)——桥的运用
- bzoj 1969: [Ahoi2005]LANE 航线规划(树链剖分+线段树+最小生成树)
- 洛谷2542 [AHOI2005]航线规划 BZOJ1969
- 【BZOJ 1969】【AHOI 2005】LANE 航线规划【离线、hash、并查集、树链剖分、线段树】
- bzoj1969LANE 航线规划
- 航线规划——3103
- 不用加减乘除做加法
- 【JAVA源码分析——Java.lang】包装类源码分析
- 3 hbase基础知识
- 了解ZooKeeper
- 左右滚动jquery 插件 swiper
- ahoi2005 lane 航线规划
- 226. Invert Binary Tree 翻转二叉树
- 17. Letter Combinations of a Phone Number
- 详细解读Jquery各Ajax函数:$.get(),$.post(),$.ajax(),$.getJSON()
- 运动目标跟踪(一)--搜索算法预测模型之KF,EKF,UKF
- 生成全排列示例
- Android UI之switch的thumb与track属性定制自己的switch
- FtpGetFile函数下载不到最新文件Solution
- 运动目标跟踪(二)--搜索算法预测模型之粒子滤波