【题解-BZOJ】4668 冷战

题目:BZOJ4668 冷战

时间/内存限制:

在这里插入图片描述

题目描述:

S国在全球拥有N个军工厂,但由于规划不当,一开始这些军工厂之间是不存在铁路的,为了使武器制造更快,S国决定修建若干条道路使得某些军工厂联通。 A国得到了S国的修建日程表,并且需要时刻关注着某两个军工厂是否联通,以及最早在修建哪条道路时会联通。具体而言,现在总共有M个操作,操作分为两类: 0 u v,这次操作S国会修建一条连接u号军工厂及v号军工厂的双向铁路; 1 u v,A国需要知道u号军工厂及v号军工厂最早在加入第几条铁路后会联通,假如到这次操作都没有联通,则输出0;

输入

输入第一行为两个整数N和M(1≤N,M≤500 000)。 接下来M行,每行为0 u v或1 u v的形式。 数据是经过加密的,对于每次加边或询问,真正的u,v都等于读入的u, v异或 上一次询问的答案。一开始这个值为0。 解密后的u,v满足1≤u,v≤N,u不等于v。

输出

对于每次1的操作,输出u,v最早在加入哪条边后会联通,若到这个操作时还没联通,则输出 0。

样例输入

5 9
0 1 4
1 2 5
0 2 4
0 3 4
1 3 1
0 7 0
0 6 1
0 1 6
1 2 6

样例输出

0
3
5

代码

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

const int NUM = 5e5+10;

int N,M;
int p[NUM];
std::vector<std::vector<int>> jilu;

int findd(int x){ //找根节点
    if(p[x] != x){
        p[x] = findd(p[x]);
    }
    return p[x];
}

int main(){
    cin >> N >> M;
    // 初始化 jilu
    jilu.resize(M + 1);
    for(int i=1;i<=N;i++){
        p[i] = i;
        jilu[0].push_back(i);
    }
    int k = 0;
    int edgnum = 0;
    for(int i=1;i<=M;i++){
        int opcode,u,v;
        cin >> opcode >> u >> v;
        u = k ^ u;
        v = k ^ v;
        if(opcode == 0){
            edgnum ++;
            p[findd(u)] = findd(v);
            for(int node=1;node<=N;node++){
                jilu[edgnum].push_back(findd(node));
            }
        }
        else{
            if(findd(u) == findd(v)){
                for(int edge=1;edge<=edgnum;edge++){
                    if(jilu[edge][u - 1] == jilu[edge][v - 1]){
                        cout << edge << endl;
                        k = edge;
                        break;
                    }
                }
            }
            else{
                cout << 0 << endl;
                k = 0;
            }
        }
    }
    return 0;
}

结果

超内存了,待修改
在这里插入图片描述

代码(修改)

#include<iostream>

using namespace std;

const int MaxN = 500010;

int N,M;
int p[MaxN];
int deep[MaxN];
int timee[MaxN], edge, rankk[MaxN];

int findd(int x){
    if(x == p[x]){
        return x;
    }
    int f = findd(p[x]);
    deep[x] = deep[p[x]] + 1;
    return f;
}

void merge(int a, int b){
    edge ++;
    int x = findd(a);
    int y = findd(b);
    
    if(x == y){
        return;
    }
    
    if(rankk[x] > rankk[y]){
        p[y] = x;
        timee[y] = edge;
    }
	else{
		if(rankk[x] == rankk[y]){
		    rankk[y] ++;
		}
		p[x] = y;
		timee[x] = edge;
	}
    
    return;
}

int query(int u, int v){
    int res = 0;
    int x = findd(u);
    int y = findd(v);
    if(x != y){
        return 0;
    }
    while(u != v){
        if(deep[u] < deep[v]){
            swap(u, v);
        }
        res = max(res, timee[u]);
        u = p[u];
    }
    return res;
}

int main(){
    scanf("%d%d", &N,&M);
    for(int i=1;i<=N;i++){
        p[i] = i;
    }
    int k = 0;
    while(M--){
        int opcode,u,v;
        scanf("%d%d%d",&opcode,&u,&v);
        u ^= k;
        v ^= k;
        if(opcode == 0){
            merge(u, v);
        }
        else{
            k = query(u, v);
            printf("%d\n", k);
        }
    }
    return 0;
}

结果

在这里插入图片描述

思考

之前一直想不明白为啥要求公共祖先,直接输出两个顶点之间大的那个所用时间不就行了。后来发现如果是两个顶点来自两棵不同的树,如下图:
在这里插入图片描述
查询9和6的连接时间的时候,9保存的是9加入左树的时间,6保存的是6加入右树的时间。而这俩时间9和6还没连通,他俩联通的时间应该是左树与右树合并的时间,因此,这时需要向上找公共祖先保存的时间。

代码参考:

https://blog.csdn.net/sdz20172133/article/details/89077686

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值