航线规划——3103

来源:互联网 发布:手机淘宝宝主页慢 编辑:程序博客网 时间:2024/04/28 07:05

题意:

n个星球,m条边,Q个操作。
(1)破坏一条边;
(2)询问两个星球之间的关键路径的条数(即为一个星球到另一个星球的必经之路)。

数据范围:
30%,n<=100,m<=500,Q<=100;
60%,n<=10000,m<=30000,Q<=20000,数据中没有删边操作;
100%,n<=30000,m<=100000,Q<=40000,数据保证任何时候图都是连通,且没有重边和自环;

思路:

30暴力,这里不多说了;

60就有意思了,操作的题目常常是顺着进行维护难而倒着来操作会简单很多。(正难则反)
倒着来时,就是先将要破坏的边变成加边,即会出现树的形状(不需要解释了…),60分则随机生成这颗树,因为60分没删边。

100其实就多了最小生成树,但这样的话,加边就不单纯了,加边时,当出现环时,那么这就不是关键路径了,所以就要路径压缩一下——并查集。

#include<bits/stdc++.h>#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)#define root 1,n,1#define lson L,mid,p<<1#define rson mid+1,R,p<<1|1#define family tree[p],tree[p<<1],tree[p<<1|1]#define LL long long#define INF 0x3f3f3f3f#define N 100005#define M 200005#define S 19#define W 40005using namespace std;int n,m,mm,q,T;int ans[M];bool vis[N];int Lt[N],Rt[N],dfsList[N],D[N];//dfsint Fa[N],fa[S][N];// PCS lcavector<int>E[M];//dfsset<int>es[M];//add edgestruct Node{    int op,from,to;}A[M],B[M];struct Tree{    struct node{        int L,R,sum;    }tree[N<<2];    void build(int L,int R,int p){        tree[p].L=L,tree[p].R=R;        if(L==R){            tree[p].sum=D[dfsList[L]]-1;//            return;        }        int mid=(L+R)>>1;        build(lson),build(rson);    }    void update(int L,int R,int p){        if(tree[p].L==L && R==tree[p].R){            tree[p].sum--;            return;        }        int mid=(tree[p].L+tree[p].R)>>1;        if(R<=mid)update(L,R,p<<1);        else if(L>mid)update(L,R,p<<1|1);        else update(lson),update(rson);    }    int query(int x,int p){        if(tree[p].L==tree[p].R)return tree[p].sum;        int mid=(tree[p].L+tree[p].R)>>1;        if(x<=mid)return tree[p].sum+query(x,p<<1);        else return tree[p].sum+query(x,p<<1|1);    }}Tree;struct LCA{    void Up(int &x,int step){        REP(i,0,S-1)if(step&(1<<i))x=fa[i][x];    }    int Lca(int a,int b){        if(D[a]>D[b])swap(a,b);        Up(b,D[b]-D[a]);        if(a==b)return a;        DREP(i,S-1,0)if(fa[i][a]!=fa[i][b])a=fa[i][a],b=fa[i][b];        return fa[0][a];    }    void Init(){        REP(j,1,S-1)            REP(i,1,n)                fa[j][i]=fa[j-1][fa[j-1][i]];    }}LCA;struct PCS{    int Find(int x){return Fa[x]==x?x:Fa[x]=Find(Fa[x]);}    void Up(int &x){        Tree.update(Lt[x],Rt[x],1);        Fa[x]=fa[0][x];x=Find(x);//path compression    }    void Addedge(int x,int y){        int fx=Find(x),fy=Find(y);        while(fx!=fy)Up(D[fx]>D[fy]?fx:fy);    }    void Init(){        REP(i,1,n)Fa[i]=i;    }}PCS;void dfs(int x,int f){    D[x]=D[f]+1;    fa[0][x]=f;    Lt[x]=++T;    dfsList[T]=x;    vis[x]=1;    REP(i,0,E[x].size()-1){        int y=E[x][i];        if(y==f || es[x].find(y)!=es[x].end())continue;        if(vis[y]){B[++mm].from=x,B[mm].to=y;continue;} // find link        dfs(y,x);    }    Rt[x]=T;}void Rd(){    scanf("%d%d%d",&n,&m,&q);    REP(i,1,m){        int a,b;        scanf("%d%d",&a,&b);        E[a].push_back(b);        E[b].push_back(a);    }    REP(i,1,q){        scanf("%d%d%d",&A[i].op,&A[i].from,&A[i].to);        if(!A[i].op)es[A[i].from].insert(A[i].to),es[A[i].to].insert(A[i].from);    }}void solve(){    dfs(1,0);    LCA.Init();    PCS.Init();    Tree.build(root);    REP(i,1,mm)PCS.Addedge(B[i].from,B[i].to);      DREP(i,q,1){        if(!A[i].op)PCS.Addedge(A[i].from,A[i].to);// add edge        else ans[i]=Tree.query(Lt[A[i].from],1)+Tree.query(Lt[A[i].to],1)-2*Tree.query(Lt[LCA.Lca(A[i].from,A[i].to)],1);    }}void Pr(){    REP(i,1,q)if(A[i].op)printf("%d\n",ans[i]);}int main(){    Rd();    solve();    Pr();    return 0;}

小结:
dfs序->找到符合线段树的一种排列
线段树查询+修改
倍增求lca,并查集加边。