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
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 1岁宝宝太调皮了怎么办 孩子和同学打架家长该怎么办 孩子被大人打了怎么办 小孩不跟大人沟通怎么办 2岁小朋友爱动手怎么办 小班爱动手的小朋友怎么办 宝宝对小朋友不友好爱动手怎么办 小孩子上幼儿园爱打人怎么办 小孩被别人打了怎么办 1岁幼儿爱打人怎么办 作为幼小朋友打人老师怎么办 2岁半小朋友喜欢打人怎么办 2岁宝宝脾气大怎么办 4月小孩爱动怎么办 一岁宝宝老打人怎么办 1岁宝宝爱打人怎么办 3岁宝宝喜欢抓人怎么办 宝宝喜欢打人怎么办2岁 1岁宝宝动手打人怎么办 孩子总打人总哭怎么办 小孩出现夜惊家人怎么办 小孩不原跟家人沟通怎么办 孩子字写得难看怎么办 孩子上一年级不认识字怎么办 二年级孩子语文差怎么办 孩子二年级语文成绩差怎么办 孩子小学二年级语文差怎么办 二年级孩子语文理解能力差怎么办 深圳租房被坑了怎么办 小鸣单车押金退不了怎么办 联想台式一体机忘记密码怎么办 ps直线工具变成箭头了怎么办 笔记本图形处理速度慢怎么办 微信语音发不出去怎么办 ps里的图层锁定怎么办 ps图层丢失了怎么办 PS标题画面太小怎么办 轮胎蹭掉一块皮怎么办 吃香蕉吃的胃难受怎么办 qq糖卡在喉咙里怎么办 头发上粘到了qq糖怎么办