Luogu3774 [CTSC2017]最长上升子序列 【Young表,根号分治】

题目链接:洛谷

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

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

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

1 2 3 4
2 5 7
3 6

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

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

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

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

时间复杂度为\(O(n\sqrt{n}\log n+q\log 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\n", ans[i]);
}
posted @ 2019-10-29 14:50  mizu164  阅读(220)  评论(0编辑  收藏  举报