• 2014年第五届蓝桥杯国赛 Log大侠(区间合并+Java递归效率分析)


    1678: Log大侠 java

    时间限制: 2 Sec  内存限制: 256 MB
    提交: 20  解决: 1

    题目描述


        atm参加了速算训练班,经过刻苦修炼,对以2为底的对数算得飞快,人称Log大侠。

        一天,Log大侠的好友 drd 有一些整数序列需要变换,Log大侠正好施展法力...

        变换的规则是: 对其某个子序列的每个整数变为: [log_2 (x) + 1]  其中 [] 表示向下取整,就是对每个数字求以2为底的对数,然后取下整。
        例如对序列 3 4 2 操作一次后,这个序列会变成 2 3 2。
        
        drd需要知道,每次这样操作后,序列的和是多少。

    【输入格式】
    第一行两个正整数 n m 。
    第二行 n 个数,表示整数序列,都是正数。
    接下来 m 行,每行两个数 L R 表示 atm 这次操作的是区间 [L, R],数列序号从1开始。

    【输出格式】
    输出 m 行,依次表示 atm 每做完一个操作后,整个序列的和。

    例如,输入:
    3 3
    5 6 4
    1 2
    2 3
    1 3

    程序应该输出:
    10
    8
    6


    【数据范围】
    对于 30% 的数据, n, m <= 10^3
    对于 100% 的数据, n, m <= 10^5


    资源约定:
    峰值内存消耗 < 256M
    CPU消耗  < 1000ms


    请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

    注意: main函数需要返回0
    注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
    注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。

    提交时,注意选择所期望的编译器类型。

     

    区间问题显然线段树,but做题时想到一种区间合并做法,相对更优。

    这里采用反向求和,即先把初始和求出,然后每次操作后减去改变的差值。

    因为任何正整数经过若干次log操作后只会有两种情况1和2(除1本身不变,其余变为2)

    我们暂且名其为奇异情况,当遇到奇异情况时数不会再改变,我们可以跳过对其的操作。

    然后就是区间合并。使用并查集,遍历对应值祖先,若当前值变为奇异情况则将其合并(当前值祖先变为下个值祖先)

    笔者开始使用递归处理并查集,结果吃了次运行错误,究其原因是Java的递归效率远低于循环!

    因为在Java中每层递归都会增加额外的堆栈处理,占用过多内存,所以在比赛中应尽量减少使用递归或使用循环代替

    这次也算是教训吧,幸好在赛前发现看来要改改习惯了

    import java.util.Scanner;
     
    public class Main {
         
        static Scanner sc = new Scanner(System.in);
        static long[] a = new long[100005];
        static int[] f = new int[100005];
        static int find(int x) {    //循环式路径压缩
            int p=x;
            while(f[p]!=p){
                p=f[p];
            }
            while(x!=p){
                int t=f[x];
                f[x]=p;
                x=t;
            }
            return x;
        }
        static long cal(long x) {
            long ans=0;
            while(x>0) {
                x>>=1;
                ans++;
            }
            return ans;
        }
        public static void main(String[] args) {
             
            int n,m,l,r,i,j;
            long ans=0;
            n=sc.nextInt();
            m=sc.nextInt();
            for(i=1;i<=n;i++) {
                a[i]=sc.nextLong();
                ans+=a[i];
                f[i]=i;
            }
            f[n+1]=n+1;
            while((m--)>0) {
                l=sc.nextInt();
                r=sc.nextInt();
                for(i=find(l);i<=r;i=find(i+1)) {   //区间合并
                    long t=cal(a[i]);
                    ans-=a[i]-t;
                    a[i]=t;
                    if(a[i]<3) {
                        int fi=find(i),fi1=find(i+1);
                        if(fi!=fi1) {
                            f[fi]=fi1;
                        }
                    }
                }
                System.out.println(ans);
            }
        }
    }
  • 相关阅读:
    PS制作动感酷炫水人街舞照
    PS调出水彩画效果古装人物照片
    PS调出怀旧雨中特写的非主流照片
    PS制作墙壁上海报卷页图片效果
    PS绘制扁平化风格相机镜头UI图标
    PS调出甜美艺术外景女生照片
    PS调出冷绿色电影画面风格
    PS调出通透唯美阳光外景女生照片
    PS调出唯美冷色情侣婚纱写真照
    c#类中字段和方法中变量的声明问题
  • 原文地址:https://www.cnblogs.com/yzm10/p/10886312.html
Copyright © 2020-2023  润新知