Codeforces 466E LCA+搞

博客介绍了如何利用并查集和最近公共祖先(LCA)算法解决Codeforces的一道题目。文章通过解析题意,展示了在处理组织结构变更和文件传递查询时,如何应用并查集进行合并操作,并利用LCA快速查找节点间的关系,以确定文件是否经过特定节点签名。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
*Rainto96
*Beijing University of Posts and Telecommunications School of Software Engineering
*http://blog.youkuaiyun.com/u011775691
题意:
给出10万个点,10万个操作
操作有三种
1.x->y 指定x为y的上级
2.给x一份文件,x签名后给上级,上级签名后给上级的上级,直到没有上级
3.询问x是否签名了文件id
id按输入顺序加一,初始为1

解法:
搞
操作1就是并查集合并,然后x就是y的父亲
操作2就记录下来两个端点,一个是x,一个是当时的并查集树根
操作3就输入完所有输入建完树后,查询x是否在id文件的两个端点fa,son之间,很好判断
做一个虚拟根(可能是森林) ,然后dfs出LCA,如果x是在两个端点fa,son之间的路径上,则肯定有lca(x,fa) = fa , lca(x, son ) = x 即可
*/
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define ALL(x) x.begin(),x.end()
#define VINT vector<int>
#define PII pair<int,int>
#define MP(x,y) make_pair((x),(y))
#define ll long long
#define ull unsigned ll
#define MEM0(x)  memset(x,0,sizeof(x))
#define MEM(x,val) memset((x),val,sizeof(x))
#define scan(x) scanf("%d",&(x))
#define scan2(x,y) scanf("%d%d",&(x),&(y))
#define scan3(x,y,z) scanf("%d%d%d",&(x),&(y),&(z))
#define scan4(x,y,z,k) scanf("%d%d%d%d",&(x),&(y),&(z),&(k))
#define Max(a,b) a=max(a,b)
#define Min(a,b) a=min(a,b)
using namespace std;
const int MAXN= 111111;
int par[MAXN];
VINT G[MAXN];
int f[MAXN];
int find(int u){
        return par[u]==u?u:par[u]=find(par[u]);
}
void unit(int u,int v){//u is v's boss
        u= find(u) , v= find(v);
        if(u==v) return;
        par[v] = u;
}
int fa[33][MAXN] , depth[MAXN] , vis[MAXN];
void fafs(int now, int fat ,int dep){
        fa[0][now] = fat;
        depth[now] = dep;
        vis[now] =true;
        for(auto to:G[now]){
                if(vis[to]) continue;
                fafs(to, now ,dep+1);
        }
}

int LOG_N=20;
//计算u和v的LCA
int lca(int u,int v) {
	if(depth[u]>depth[v]) swap(u,v);
	for(int k=0;k<LOG_N;k++) if((depth[v]-depth[u])>>k&1) v=fa[k][v];
	if(u==v) return u;
	for(int k=LOG_N-1;k>=0;k--)if(fa[k][u]!=fa[k][v]) u=fa[k][u] , v=fa[k][v];
	return fa[0][u];
}


PII v[MAXN];
int main(){
	#ifndef ONLINE_JUDGE
		//freopen("C:/OJ/in.txt","r",stdin);
	#endif
	int n ,m;
	scan2(n,m);
	for(int i=1;i<=n;i++) par[i]= i;
	int doc = 1;
	vector<PII > que;
	for(int i=1;i<=m;i++){
                int t;scan(t);
                if(t==1){
                        int x,y;scan2(x,y);
                        unit(y,x);
                        G[y].pb(x);
                }else if(t==2){
                        int x; scan(x);
                        int fa = find(x);
                        //v.pb(MP(fa,MP(x,doc++)));
                        v[doc++]=MP(fa,x);
                }else{
                        int x,id;scan2(x,id);
                        que.pb(MP(x,id));
                }
	}
	int root=0;
	for(int i=1;i<=n;i++){
                G[root].pb(find(i));
	}
        fafs(root ,-1 ,0);

        for(int k=0;k+1<LOG_N;k++)for(int v=1;v<=n;v++)
                if(fa[k][v]<0) fa[k+1][v]=-1;
                else fa[k+1][v]=fa[k][fa[k][v]];

        for(int i=0;i<que.size();i++){
                int x = que[i].first , id= que[i].second;
                int fat = v[id].first  , son = v[id].second;
                if(lca(fat,x)==fat && lca(x,son)==x){
                        puts("YES");
                }else puts("NO");
        }

        return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值