「NOI2002」 银河英雄传说

「NOI2002」 银河英雄传说对于这道题 首先不要被它的 NOI 身份 就是个古早的 t1 和较长的描述 实际也不长 吓到

题意

有两个名为xxx的神奇数据结构,xxx A A A 有最多 30000 30000 30000 个点,开始每行都只有一个点。

有时会给出合并指令,格式为 M M M i i i j j j,每次都将处在第 i i i个点所在行整体移到第 j j j 个点所在行的最后。

有时会给出查询指令,格式为 C C C i i i j j j,询问。

分析

对于这道题,首先不要被它的NOI身份(就是个古早的t1)和较长的描述(实际也不长)吓到。

仔细观察,因为要用并查集,所以可以把每一行的第一个点就可以看成根节点,这就具备了并查集的基本形状。

可以把问题分成几个小问题:

  1. 记录每个点在哪一行:因为这是并查集的题,所以可以用并查集实现。
  2. 计算同一行内两个点的距离:执行 f i n d find find 操作时,因为要对 p r e pre pre 数组进行路径压缩(不然容易炸空间&时间),所以较难直接用 p r e pre pre 求解,再开一个 d i s dis dis 数组,直接记录每一个点到它的根节点的距离,需要时直接做差求绝对值就好了。
  3. 实现题目中的合并操作:一般并查集的合并操作是直接合并每棵树的根节点(改变 p r e pre pre 数组),不会改变其他如 d i s dis dis 的值,所以可以用 n u m num num 数组,记录每一排的节点数,每次合并时直接在 d i s i dis_i disi 上加 n u m j num_j numj,就可以对同一排内的点进行顺序区分。

可以看到,每次 d i s i dis_i disi 变化时,都会加上 n u m j num_j numj,但 p r e f i n d ( i ) pre_{find(i)} prefind(i) 可以直接连到 p r e f i n d ( j ) pre_{find(j)} prefind(j) 上,这样就实现了合并。

那么查询其实就是求从 i i i j j j 的节点数,因为有 d i s dis dis 记录两点到第一个点的距离,就可以直接转化为求 a b s ( d i s i − d i s j ) abs(dis_i-dis_j) abs(disidisj)

end.

Code

#include<bits/stdc++.h> using namespace std; char f; int t,a,b,x,y,pre[30001],dis[30001],num[30001]; int find(int x){ 
   //带权并查集的路径压缩find if (pre[x]==x)return x; int sum=find(pre[x]); dis[x]+=dis[pre[x]]; return pre[x]=sum; } int main(){ 
    scanf("%d",&t); for (int i=1;i<=30000;++i){ 
    pre[i]=i,num[i]=1; } for (int i=1;i<=t;++i){ 
    cin>>f>>a>>b; x=find(a),y=find(b); if (f=='M'){ 
    dis[x]=num[y];//x点到了末尾 num[y]+=num[x];//b排要加上a排原数 num[x]=0;//a排已经并到b排,所以没有战舰 pre[x]=y;//真正的合并 } else{ 
    if (x!=y){ 
    puts("-1"); } else{ 
    printf("%d\n",abs(dis[a]-dis[b])-1); } } } } 
今天的文章 「NOI2002」 银河英雄传说分享到此就结束了,感谢您的阅读。
编程小号
上一篇 2024-12-14 09:06
下一篇 2024-12-14 09:01

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/86331.html