小魂和他的数列-(离散+二分+树状数组)

https://ac.nowcoder.com/acm/problem/54585

题意:给500000个数构成一个数列,求递增个数为k的子序列个数,2<=k<=10。

题解:

1.求递增子序列个数,子序列不是子串,可以散乱分布。原数组为a,排序后为数组b,遍历a数组,每次求得ai在数组b的下标位置,设为pos,在树状数组里对pos位置进行累加,维护k个树状数组,递增个数为j的子序列 需要 递增个数为j-1的子序列的数据。c[i][j]表示的递增个数为i的子序列在j这个位置的个数。

2.一开始很难理解为什么要这样做,在纸上比划了许多下才懂,树状数组中还没有出现的值都是0,和逆序对有些相似。

3.大量数据输入用Java解题需要自行封装输入模板

import java.io.*;
import java.util.StringTokenizer;
import java.math.BigInteger;
import java.util.Arrays;

public class Main {
    static int [] a=new int[500010];
    static int [] b=new int[500010];
    static int [][] c=new int[11][500010];
    static int n=0;
    static int p=998244353;

    public static void main(String[] args) {
        InputStream inputStream = System.in;//InputStream是表示字节输入流的所有类的超类
        OutputStream outputStream = System.out;

        InputReader sc = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);

        Task solver = new Task();
        solver.solve(sc, out);//这里当作原来的Main函数,输入输出都在里面解决

        out.close();//关闭输出流
    }

    static class Task {    

        public void solve(InputReader scan, PrintWriter out) {
            n=scan.nextInt();
            int k=scan.nextInt();
            for(int i=1;i<=n;i++) {
                a[i]=scan.nextInt();
                b[i]=a[i];
            }
            Arrays.sort(b,1,n+1);
            for(int i=1;i<=n;i++) {
                int pos=check(1, n,a[i]);
                //System.out.println("pos="+pos);
                add(1, pos, 1);
                for(int j=2;j<=Math.min(k, i);j++) {
                    add(j, pos, get_sum(j-1, pos-1));
                }
            }
            System.out.println(get_sum(k, n));

        }

        public static int lowbit(int x) {
            return (-x)&x;
        }

        public static void add(int i,int x,int val) {
            while(x<=n) {
                c[i][x]=(c[i][x]+val)%p;
                x=x+lowbit(x);
            }

        }

        public static int get_sum(int i,int x) {
            int res=0;
            while(x!=0) {
                res=(res+c[i][x])%p;
                x-=lowbit(x);
            }
            return res;
        }

        //二分 起始都是false,如果要找的x相同,先找一段相同的数的最左边,然后标记
        public static int check(int l,int r,int x) {
            int mid=-1;
            while(l<=r) {
                mid=(l+r)/2;
                //System.out.println("x="+x+" l="+l+" r="+r+" mid="+mid);
                if(b[mid]>x) //往左
                    r=mid-1;
                else if(b[mid]<x) //往右
                    l=mid+1;
                else
                    break;

            }
            return mid;
        }
    }

   //自己写出Scanner原本的输入语法,封装在InputReader类里
    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;
        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            //32768是输入缓冲区大小,随便设的
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public double nextDouble() {
            return Double.parseDouble(next());
        }

        public boolean hasNext() {
            try {
                String string = reader.readLine();
                if (string == null) {
                    return false;
                }
                tokenizer = new StringTokenizer(string);
                return tokenizer.hasMoreTokens();
            } catch (IOException e) {
                return false;
            }
        }

        public BigInteger nextBigInteger() {//大数
            return new BigInteger(next());
        }

    }
}

原文地址:https://www.cnblogs.com/shoulinniao/p/12236151.html

时间: 2024-10-01 20:13:01

小魂和他的数列-(离散+二分+树状数组)的相关文章

【bzoj2527】[Poi2011]Meteors 整体二分+树状数组

题目描述 有N个成员国.现在它发现了一颗新的星球,这颗星球的轨道被分为M份(第M份和第1份相邻),第i份上有第Ai个国家的太空站. 这个星球经常会下陨石雨.BIU已经预测了接下来K场陨石雨的情况.BIU的第i个成员国希望能够收集Pi单位的陨石样本.你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石. 输入 第一行是两个数N,M. 第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站. 第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量. 第四行有一个

11525 - Permutation(二分+树状数组)

题目链接:点击打开链接 题意:从1~k的所有排列中找到第n个排列, n由公式给出. 思路:可以发现, 这个公式就是康托展开公式(康托展开百科:点击打开链接). 那么s[i]的意思就是i个数中当前数排在第几. 如此, 可以用二分+树状数组快速求解, 和一道BC题目神似. 细节参见代码: #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<st

Codeforces 374D Inna and Sequence 二分+树状数组

题目链接:点击打开链接 给定n个操作,m长的序列a 下面n个数 if(co>=0)则向字符串添加一个co (开始是空字符串) else 删除字符串中有a的下标的字符 直接在序列上搞,简单模拟 #include<stdio.h> #include<iostream> #include<string.h> #include<set> #include<vector> #include<map> #include<math.h&

【51nod】 第K大区间2(二分+树状数组)

[51nod] 第K大区间2(二分+树状数组) 第K大区间2 ﹡    LH (命题人) 基准时间限制:1.5 秒 空间限制:131072 KB 分值: 160 定义一个长度为奇数的区间的值为其所包含的的元素的中位数.中位数_百度百科 现给出n个数,求将所有长度为奇数的区间的值排序后,第K大的值为多少. 样例解释: [l,r]表示区间的值 [1]:3 [2]:1 [3]:2 [4]:4 [1,3]:2 [2,4]:2 第三大是2 Input 第一行两个数n和k(1<=n<=100000,k&l

【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询

Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Output 输出每个询问的结果 Sample Input 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 Sample Output 1 2 1 HINT

【BZOJ-2527】Meteors 整体二分 + 树状数组

2527: [Poi2011]Meteors Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 831  Solved: 306[Submit][Status][Discuss] Description Byteotian Interstellar Union (BIU) has recently discovered a new planet in a nearby galaxy. The planet is unsuitable for colo

【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改

题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. 输入 第一行N,M接下来M行,每行形如1 a b c或2 a b c 输出 输出每个询问的结果 样例输入 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 样例输出 1 2 1 题解 整体二分+树状数组区间修改 当年naive的树套树题解 前两天由于要

Codeforces Round #470 (Div 2) B 数学 C 二分+树状数组 D 字典树

Codeforces Round #470 B. Primal Sport 数学题,对 x2 和 x1 分解质因子即可. #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b;

hdu-5714 拍照(二分+树状数组)

题目链接: 拍照 Time Limit: 6000/3000 MS (Java/Others)     Memory Limit: 65536/65536 K (Java/Others) Problem Description 小明在旅游的路上看到了一条美丽的河,河上有许多船只,有的船只向左航行,有的船只向右航行.小明希望拍下这一美丽的风景,并且把尽可能多的船只都完整地拍到一张照片中. 小明位于河的边上,并且可以在河边的任意位置进行拍照,照相机的视野恰好为90度角,只能以垂直于河边的方向进行拍照