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

Posted shoulinniao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小魂和他的数列-(离散+二分+树状数组)相关的知识,希望对你有一定的参考价值。

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());
        }
 
    }
}

 

以上是关于小魂和他的数列-(离散+二分+树状数组)的主要内容,如果未能解决你的问题,请参考以下文章

NC54585 小魂和他的数列

牛客练习赛7 E 珂朵莉的数列

POJ 2299 Ultra-QuickSort(树状数组 + 离散)

[luogu4479][BJWC2018]第k大斜率二维偏序+二分+离散化+树状数组

2019 Multi-University Training Contest 3 Find the answer (离散化+二分+树状数组)

浅析树状数组