题目描述

公元五八零一年,地球居民迁至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展。

宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争。泰山压顶集团派宇宙舰队司令莱因哈特率领十万余艘战舰出征,气吞山河集团点名将杨威利组织麾下三万艘战舰迎敌。

杨威利擅长排兵布阵,巧妙运用各种战术屡次以少胜多,难免恣生骄气。在这次决战中,他将巴米利恩星域战场划分成30000列,每列依次编号为1, 2, …,30000。之后,他把自己的战舰也依次编号为1, 2, …, 30000,让第i号战舰处于第i列(i = 1, 2, …, 30000),形成“一字长蛇阵”,诱敌深入。这是初始阵形。当进犯之敌到达时,杨威利会多次发布合并指令,将大部分战舰集中在某几列上,实施密集攻击。合并指令为M i j,含义为第i号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第j号战舰所在的战舰队列的尾部。显然战舰队列是由处于同一列的一个或多个战舰组成的。合并指令的执行结果会使队列增

大。 然而,老谋深算的莱因哈特早已在战略上取得了主动。在交战中,他可以通过庞大的情报网络随时监听杨威利的舰队调动指令。

在杨威利发布指令调动舰队的同时,莱因哈特为了及时了解当前杨威利的战舰分布情况,也会发出一些询问指令:C i j。该指令意思是,询问电脑,杨威利的第i号战舰与第j号战舰当前是否在同一列中,如果在同一列中,那么它们之间布置有多少战舰。

作为一个资深的高级程序设计员,你被要求编写程序分析杨威利的指令,以及回答莱因哈特的询问。

最终的决战已经展开,银河的历史又翻过了一页……

 

好久之前做的题了。

加权并查集。大概就是用Tail[i]记录集合i的队尾是谁,k[i]是战舰i在集合中的权(也就是与root的距离),合并的时候根据tail调整就行了。

貌似很傻逼的样子。

int findx(int e)
{
    if (e!=fatherx[e])
    {
        int root=findx(fatherx[e]);
        k[e]+=k[fatherx[e]];
        fatherx[e]=root;
    }
    return fatherx[e];
}

void mergex(int e1,int e2)
{
    int root1=findx(e1);
    int root2=findx(e2);
    
    k[root1]=1;//update the length of E1's root ( find the tail of E2 )
    fatherx[root1]=tail[root2];//合并 
    tail[root2]=tail[root1];//更新E2的tail为E1的tail 
}

更新root的时候需要注意k[e]记录的是当前结点与当前根的距离,所以对于压缩路径之后新根,是fatherx[e]对新根的距离+e对fatherx[e]的距离

对于所有加权并查集的题目,建议画图

merge的时候注意更新tail数组。设A2战舰集合接到A1的队尾,那么需要先更新A2的root的父亲结点为A1的队尾结点(tail[root_of_A1])然后再更新tail[root_of_A1]为tail[root_of_A2]。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<iterator>
#include<utility>
using namespace std;

int t,fatherx[30010],k[30010]={0},tail[30010]={0},cachex,cachey;
char cachecom;

void readx(int &x)
{
    x=0; register char ch=0;
    while (ch<'0' || ch>'9') ch=getchar();
    while (ch>='0' && ch<='9') { x=x*10+ch-'0'; ch=getchar(); }
}

int findx(int e)
{
    if (e!=fatherx[e])
    {
        int root=findx(fatherx[e]);
        k[e]+=k[fatherx[e]];
        fatherx[e]=root;
    }
    return fatherx[e];
}

void mergex(int e1,int e2)
{
    int root1=findx(e1);
    int root2=findx(e2);
    
    k[root1]=1;//update the length of E1's root ( find the tail of E2 )
    fatherx[root1]=tail[root2];//合并 
    tail[root2]=tail[root1];//更新E2的tail为E1的tail 
}

int main()
{
    for (int i=1;i<=30000;i++) fatherx[i]=tail[i]=i;
    readx(t);
    for (int i=1;i<=t;i++)
    {
        while (cachecom!='M' && cachecom!='C') cachecom=getchar();
        readx(cachex); readx(cachey);
        if (cachecom=='M' && findx(cachex)!=findx(cachey)) mergex(cachex,cachey);
        else
        {
            if (findx(cachex)!=findx(cachey)) printf("-1\n");
            else
            {
                findx(cachex); findx(cachey);
                printf("%d\n",abs(k[cachex]-k[cachey])-1);
            }
        }
        cachecom=0;
    }
    return 0;
}

整体上来看就这些东西,不是很难。


发表评论

电子邮件地址不会被公开。 必填项已用*标注