zoukankan      html  css  js  c++  java
  • Luogu3774 [CTSC2017]最长上升子序列 【Young表,根号分治】

    题目链接:洛谷

    推荐阅读:2019年集训队论文《浅谈杨氏矩阵在信息学竞赛中的应用》

    首先我们来看一个东西,叫做Young表。

    它是长一个阶梯状的东西(行长和列长都是递减的),并且每一行和每一列都是按照一个不等号单调的。比如这是一个单调上升的Young表。

    1 2 3 4
    2 5 7
    3 6
    

    那么有一个序列转化为Young表的(按行)插入算法(Sleftarrow x)(以单调不升为例):

    在当前行找第一个(le x)且最小的数(p),若这个位置是空,那么直接将(x)放到行末,否则将(x,p)交换,然后插入下一行。一个个插入就能得到一个 Young 表。

    不知道为什么,这个序列的前(k)行就是在这个序列里面取(k)个单调不上升子序列的使得总长最大。根据 Dilworth 定理,前(k)行的子序列就是最长上升子序列为(k)的最长子序列。所以询问的就是 Young 表的前(k)行的元素个数。

    我们发现 Young 表插入算法的时间复杂度是(O(rlog c))的,一看就很不平衡的样子。然后有一个结论就是,如果改为单调上升的 Young 表,那么这两个 Young 表互为转置(或者说按列插入和按行插入得到的结果是一样的)。我们知道这个 Young 表的每个元素的横坐标和纵坐标不可能同时(>sqrt{n}),所以维护这两个 Young 表的前(sqrt n)行,用树状数组维护每行元素个数的前缀和。

    时间复杂度为(O(nsqrt{n}log n+qlog n))

    #include<bits/stdc++.h>
    #define Rint register int
    using namespace std;
    const int N = 200005, M = 255, inf = 0x3f3f3f3f;
    int n, q, siz, b[N], ans[N], tr[N];
    inline int lowbit(int x){return x & -x;}
    inline void change(int pos, int val){while(pos < N){tr[pos] += val; pos += lowbit(pos);}}
    inline int query(int pos){int ans = 0; while(pos){ans += tr[pos]; pos -= lowbit(pos);} return ans;}
    struct Table {
    	vector<int> vec[M];
    	int sign;
    	inline void insert(int x, int len, int p){
    		if(x > siz) return;
    		int l = 0, r = min(len, (int) vec[x].size()), mid;
    		while(l < r){
    			mid = l + r >> 1;
    			if(sign ^ (vec[x][mid] < p)) r = mid;
    			else l = mid + 1;
    		}
    		if(r == vec[x].size()){
    			vec[x].push_back(p);
    			if(sign){if(r >= siz) change(r + 1, 1);}
    			else change(x, 1);
    		} else {
    			swap(vec[x][r], p);
    			insert(x + 1, r, p);
    		}
    	}
    } T1, T2;
    struct Query {
    	int m, k, id;
    	inline bool operator < (const Query &o) const {return m < o.m || (m == o.m && k < o.k);}
    } que[N];
    int main(){
    	T1.sign = 0, T2.sign = 1;
    	scanf("%d%d", &n, &q); siz = sqrt(n);
    	for(Rint i = 1;i <= n;i ++) scanf("%d", b + i);
    	for(Rint i = 1;i <= q;i ++) scanf("%d%d", &que[i].m, &que[i].k), que[i].id = i;
    	sort(que + 1, que + q + 1);
    	for(Rint i = 1, j = 1;i <= n;i ++){
    		T1.insert(1, inf, b[i]); T2.insert(1, inf, b[i]);
    		while(j <= q && que[j].m == i) ans[que[j].id] = query(que[j].k), ++ j;
    	}
    	for(Rint i = 1;i <= q;i ++) printf("%d
    ", ans[i]);
    }
    
  • 相关阅读:
    Gecko Bootloader的介绍(Silicon Labs)【一】
    使用模板新建ZigBee工程的方法
    代码控制ZigBee网络密钥的生成
    Ubuntu20编译最新版Android源码教程
    C和C++常用代码片段整理
    Java易错的知识点整理
    仿IntelliJ Darcula的Swing主题FlatLaf使用方法
    PuTTYTabManager汉化版
    WinSCP整合SecureCRT打开终端
    异想家博客图片批量压缩程序
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/11758722.html
Copyright © 2011-2022 走看看