小魂和他的数列-(离散+二分+树状数组)
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()); } } }
以上是关于小魂和他的数列-(离散+二分+树状数组)的主要内容,如果未能解决你的问题,请参考以下文章
POJ 2299 Ultra-QuickSort(树状数组 + 离散)
[luogu4479][BJWC2018]第k大斜率二维偏序+二分+离散化+树状数组
2019 Multi-University Training Contest 3 Find the answer (离散化+二分+树状数组)