• 洛谷 P1196 银河英雄传说


    题目描述

    公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦

    创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展。

    宇宙历七九九年,银河系的两大军事集*在巴米利恩星域爆发战争。泰山压

    顶集**宇宙舰队司令莱因哈特率领十万余艘战舰出征,气吞山河集*点名将杨

    威利组织麾下三万艘战舰迎敌。

    杨威利擅长排兵布阵,巧妙运用各种战术屡次以少胜多,难免恣生骄气。在

    这次决战中,他将巴米利恩星域战场划分成30000列,每列依次编号为1, 2, …,

    30000。之后,他把自己的战舰也依次编号为1, 2, …, 30000,让第i号战舰处于

    第i列(i = 1, 2, …, 30000),形成“一字长蛇阵”,诱敌深入。这是初始阵形。当

    进犯之敌到达时,杨威利会多次发布合并指令,将大部分战舰集中在某几列上,

    实施密集攻击。合并指令为M i j,含义为让第i号战舰所在的整个战舰队列,作

    为一个整体(头在前尾在后)接至第j号战舰所在的战舰队列的尾部。显然战舰

    队列是由处于同一列的一个或多个战舰组成的。合并指令的执行结果会使队列增

    大。 然而,老谋深算的莱因哈特早已在战略上取得了主动。在交战中,他可以通

    过庞大的情报网络随时监听杨威利的舰队调动指令。

    在杨威利发布指令调动舰队的同时,莱因哈特为了及时了解当前杨威利的战

    舰分布情况,也会发出一些询问指令:C i j。该指令意思是,询问电脑,杨威利

    的第i号战舰与第j号战舰当前是否在同一列中,如果在同一列中,那么它们之

    间布置有多少战舰。

    作为一个资深的高级程序设计员,你被要求编写程序分析杨威利的指令,以

    及回答莱因哈特的询问。

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

    输入输出格式

    输入格式:

     

    输入文件galaxy.in的第一行有一个整数T(1<=T<=500,000),表示总共有T

    条指令。

    以下有T行,每行有一条指令。指令有两种格式:

    1. M i j :i和j是两个整数(1<=i , j<=30000),表示指令涉及的战舰编号。

    该指令是莱因哈特窃听到的杨威利发布的舰队调动指令,并且保证第i号战

    舰与第j号战舰不在同一列。

    1. C i j :i和j是两个整数(1<=i , j<=30000),表示指令涉及的战舰编号。

    该指令是莱因哈特发布的询问指令。

     

    输出格式:

     

    输出文件为galaxy.out。你的程序应当依次对输入的每一条指令进行分析和

    处理:

    如果是杨威利发布的舰队调动指令,则表示舰队排列发生了变化,你的程序

    要注意到这一点,但是不要输出任何信息;

    如果是莱因哈特发布的询问指令,你的程序要输出一行,仅包含一个整数,

    表示在同一列上,第i 号战舰与第j 号战舰之间布置的战舰数目。如果第i 号战

    舰与第j号战舰当前不在同一列上,则输出-1。

     

    输入输出样例

    输入样例#1:

    4
    M 2 3
    C 1 2
    M 2 4
    C 4 2

    输出样例#1:

    -1
    1

     

    解题思路

    不用说肯定用并查集,貌似就是不进行路径压缩的无脑模拟?不对,500000个操作,30000艘船,不超时才怪!
     
    那怎么办呢?一路径压缩战舰顺序就被改变,怎么才能在路径压缩的同时随时得知同一舰队中两艘战舰的位置?
     
    输入是合并与询问两艘战舰之间的“距离”,遇到问题是路径压缩后“距离”(间隔战舰数量)变了,询问不好维护了,但不压缩太慢了,那我们不就可以再开一个数组,存下需要的“距离”了吗?这个数组存的距离就是在路径压缩时变化了的那个:第i艘到第fa[i]艘之间的战舰数量,数组名就命名成front吧,因为路径压缩全部完成,即同一舰队中所有元素的fa[i]都等于这个舰队第一艘战舰时,front[i]=第i艘战舰前方有多少战舰(这么搞就像前缀和,路径压缩时可以一层一层地边压缩边修正下去)。

    易知在还未进行路径压缩时对于同一舰队非第一艘战舰,front[i]=1,第一艘战舰front[i]=0(一个舰队的第一艘,不是编号为一的那艘),路径压缩首先不断向fa[i]走fa[i]=find(fa[i]),走到队首,fa[i]==i,front[i]=0不变,返回队首的编号,回溯至递归上一层,把队首的编号那么第二艘的front增加0就是1,为何是增加呢?因为前面说过完成路径压缩即find函数跑完后front[i]的值就是编号i的战舰前方有几艘战舰,路径压缩前则是到fa[i]的距离,一路径压缩,就相当于fa[i]直接越过front[fa[i]]艘战舰,从i的前一艘指向队首,fa[i]前进那么多,front[i]自然也要增加那么多,修改之后继续回溯,同理第三层front[i]+=front[fa[i]]……路径压缩完成。

    合并时怎么办呢,定义合并函数uni(x,y)表示将x所在那列移到y所在那列后面(千万别搞反了),那么我们就要先找到两列的队首(依然用x、y存),像普通并查集那样fa[x]=y,然后维护front数组,这时遇到问题啦——front[y]要加多少呢?显然是x那列的战舰数,难道还要循环一遍统计一下吗?那太慢了,存下来吧,于是num[i]表示编号i这列的战舰总数(i是队首,不然每合并一次要修改的太多了,查询时num[i]时先find(i)找到队首吧),front[y]+=num[x],num[x]+=num[y],合并完成。

    还有一个问题就是询问。对于一组询问ask(x,y),先找到他们的队首fx=find(x);fy=find(y);(顺便把路径压缩进行完全了,不用担心front[i]被重复增加了,路径压缩完全时front[fa[i]]==0,因为fa[i]就是队首呀),然后判断fx!=fy就输出-1,否则就输出abs(front[x]-front[y])-1(到队首的距离之差减一就是他们间隔距离)。

    最后就只剩一个问题啦——上代码
    #include<stdio.h>
    #include<math.h>//洛谷的港记告诉我这里没有abs(),是我用的不对吗…………
    int fa[30010],num[30010]={0},front[30010]={0};
    inline int abs(int a)
    {
        return a>0?a:(-a);
    }
    int find(int x)
    {
        if(x==fa[x]) return x;
        int t=find(fa[x]);
        front[x]+=front[fa[x]];
        fa[x]=t;
        return fa[x];
    }
    void uni(int x,int y)
    {
        x=find(x);y=find(y);
        fa[x]=y;
        front[x]+=num[y];
        num[y]+=num[x];
    }
    void ask(int x,int y)
    {
         int fx=find(x);
         int fy=find(y);
         if(fx==fy)
             printf("%d
    ",abs(front[x]-front[y])-1);
         else printf("-1
    ");
    }
    
    int main()
    {
        //freopen("test.in","r",stdin);
        int q;
        char c;
        int x,y;
        scanf("%d
    ",&q);
        for(int i=0;i<30010;i++)
        {
            fa[i]=i;
            num[i]=1;
        }
        for(int i=0;i<q;i++)
        {
            scanf("%c %d %d
    ",&c,&x,&y);
            if(c=='M') uni(x,y);
            else ask(x,y);
        }
        return 0;
    }
  • 相关阅读:
    HTTP 筛选器 DLL C:WindowsMicrosoft.NetFrameworkv4.0.30319aspnet_filter.dll 加载失败。数据是错误。
    win7(iis7)无法加载运行CSS文件的解决方法
    MVC异步消息推送机制
    查看目录下所有文件的行数
    解决 mac全屏时不能隐藏Dock工具栏 killall Dock
    jetty中传java参数(spring-io中的配置项)
    nginx代理前端项目
    【转】mackbook wifi卡死未响应的问题
    WeekMap WeakSet的用途
    每日新知2019-08-23
  • 原文地址:https://www.cnblogs.com/wawcac-blog/p/6821770.html
Copyright © 2020-2023  润新知