【题解】【BZOJ】4668 冷战

大城市里の小女人 提交于 2019-12-04 08:44:55

题外话:

你们知道想了半天思路提供给同学然后证明了半天时间复杂度然后考试结束的痛苦吗……


看到连通性,我们会本能地想起并查集(想起LCT的说明您太强了orz)

题目要求输出的是\((u,v)\)这一点对最早在什么时候联通

考虑连边操作

显然,如果\((u,v)\)已经联通,那么连接它们两个是没有意义的

如果\((u,v)\)不连通,则必须连接它们两个

经过一番思考,我们思考出来以下思路:

若连接\((u,v)\),则将\(u\)并查集合并到\(v\)并查集上

连出来的边设一个边权\(val\),val为当前为第几个铁路

好像就没有了?

对于询问,我们直接往上一层层跳,跳出LCA,然后统计答案


在实际实现的时候,每个点会记录一个\(v[i]\),表示i到fa[i]这条路径上的权值

同时,为了方便,我(在参考lzh的代码后)选取了另外一种更暴力的找LCA的方法

注意:不能路径压缩,为了保证时间复杂度我们使用按高度合并(学名不知道……)
期望?均摊?反正层数\(O(logn)\)


code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=500010;
const int inf=2e9;

inline void read(int &x) {
    x=0;
    int f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {
        if (ch=='-') {
            f=-1;
        }
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x=x*10+ch-'0';
        ch=getchar();
    }
    x*=f;
}

/*
f[i]:常规并查集记录fa
v[i]:从i到f[i]这条边的权值
h[i]:i所在子树的高度
dep[i]:i所在子树中i的深度
 */
int f[N],v[N],h[N];
int dep[N];
int n,m;

 inline int find(int x) {
    while(x!=f[x]) {
        x=f[x];
    }
    return x;
}

inline int check(int x,int y) {
    int fx=find(x),fy=find(y);
    return fx==fy;
}

inline void merge(int x,int y,int val) {
    int fx=find(x),fy=find(y);
    //x所在子树高度小于y所在子树高度->将x合并到y
    if (h[fx]<h[fy]) {
        f[fx]=fy;
        h[fy]=max(h[fy],h[fx]+1);
        v[fx]=val;
    } else {
        f[fy]=fx;
        h[fx]=max(h[fx],h[fy]+1);
        v[fy]=val;
    }
}
//vis[i]:i在搜索过程中是否被找过
int vis[N];

inline int find(int x,int val) {
    while(x!=f[x]) {
        vis[x]=val;
        x=f[x];
    }
    vis[x]=val;
    return x;
}

inline int query(int x,int y) {
    find(x,1);
    int lca=y;
    while(!vis[lca]&&f[lca]!=lca) {
        lca=f[lca];
    }
    if (!vis[lca]) {
        find(x,0);
        return 0;
    }
    find(x,0);
    int ans=0;
    while(x!=lca) {
        ans=max(ans,v[x]);
        x=f[x];
    }
    while(y!=lca) {
        ans=max(ans,v[y]);
        y=f[y];
    }
    return ans;
}

int main() {
    read(n),read(m);
    for(int i=1;i<=n;i++) {
        f[i]=i;
        v[i]=-inf;
        h[i]=dep[i]=1;
    }
    int last_ans=0,cnt=0;
    for(int i=1;i<=m;i++) {
        int opx,x,y;
        read(opx),read(x),read(y);
        x^=last_ans,y^=last_ans;
        if (!opx) {
            cnt++;
            if (check(x,y)) {
                continue;
            }
            merge(x,y,cnt);
        } else {
            printf("%d\n",last_ans=query(x,y));
        }
    }
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!