slojP2577. 食物链
题目大意
一个序列初始编号为 1,2,3,,,30000
有 2 个操作:
m i j 合并第 i 列和第 j 列,将第 i 列头部接到第 j 列尾部
c I j 询问 i 号和 j 号之间的数量,若不在同一列则输出-1
第一行有一个整数T(1≤T≤5×105),表示总共有T条指令。
先分析一波
这个数据规模来看,必定是nlogn的复杂度
也就是说每个操作都是logn级别的才行
好像说了堆废话
这时,再结合题面中所给的合并操作,不难想到并查集
但是,明显普通并查集不好解决这个问题
那就用带权并查集啊
合并操作并不难,不是本题代码的关键,不过是思路中的关键
我们先考虑查询:本题难点是要查询 i,j 之间的数量而不是哪些数,
i 列和 j 列合并时,如果知道 i,j 列的数量,
则合并后,j 列中的数不受影响,i 列的每个数前面的数的个 数增加 j 列的总数。
我们需要知道每列的信息,用并查集来维护每列,
并查集每个集合维护两个值:集合总 数 count、集合中元素前面有多少元素 bef。
显然,开始时,count[i] = 1,bef[i] = 0
到此为止,基本上已经可以构建出一个框架了
最主要的函数则是并查集的find_()和merge()操作,
后者因为题目里的一些原因,被我写在了主函数里,可自行寻找
#include<bits/stdc++.h> using namespace std; int t,fa[30010],cnt[30010],bef[30010]; int find_(int x){ if(fa[x]==x)return x; int fx = find_(fa[x]); bef[x]+=bef[fa[x]]; return fa[x] = fx; } int main(){ scanf("%d",&t); for(int i = 1;i<=30000;i++){ fa[i] = i; cnt[i] = 1; } for(int i = 1;i<=t;i++){ int x,y;char ch; cin>>ch; scanf("%d%d",&x,&y); int fx = find_(x); int fy = find_(y); if(ch=='M'){ fa[fx] = fy; bef[fx] = bef[fy]+cnt[fy]; cnt[fy]+=cnt[fx]; }else{ if(fx!=fy) puts("-1"); else printf("%d\n",abs(bef[x]-bef[y])-1); } } return 0; }
非常的简短噢
标签:洛谷,fa,int,fy,查集,bef,fx,NOI2002,P1196 From: https://www.cnblogs.com/cztq/p/17002475.html