矩阵最长上升子序列java_【LOJ】#2265. 「CTSC2017」最长上升子序列

本文介绍了一种使用杨表解决矩阵最长上升子序列问题的方法,通过优化只维护前$sqrt{n}$行,并利用杨表的转置特性来减少复杂度。文章详细阐述了二分查找插入位置的过程,并提供了相关的C++代码实现。
摘要由CSDN通过智能技术生成

题解

点了一个新技能叫杨表(事实上集训的时候听过,但是一直不会

这道题就是让我们找到k个不上升子序列,要求长度加和最大

我们用杨表去维护,但是由于杨表的行数可能是n的,复杂度会炸 我们只维护前$\sqrt{n}$行 有个结论是把杨表排序方式改过来那么我们会得到杨表的转置,我们发现我们只需要求一些列的和就好了,这个转置后的也只需要维护到$\sqrt{n}$行即可

二分找每行的插入位置即可$O(n\sqrt{n} \log n + k \sqrt{n})$

代码

#include

#define fi first

#define se second

#define pii pair

#define space putchar(' ')

#define enter putchar('\n')

#define MAXN 50005

//#define ivorysi

using namespace std;

typedef long long int64;

template

void read(T &res) {

res = 0;T f = 1;char c = getchar();

while(c < '0' || c > '9') {

if(c == '-') f = -1;

c = getchar();

}

while(c >= '0' && c <= '9') {

res = res * 10 + c - '0';

c = getchar();

}

res *= f;

}

template

void out(T x) {

if(x < 0) {x = -x;putchar('-');}

if(x >= 10) out(x / 10);

putchar('0' + x % 10);

}

int N,Q,S;

int b[MAXN],ans[200005],tr[MAXN];

struct qry_node {

int pos,id,k;

friend bool operator < (const qry_node &a,const qry_node &b) {return a.pos < b.pos;}

}qry[200005];

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

void Ins(int x,int v) {

while(x <= N) {

tr[x] += v;

x += lowbit(x);

}

}

int Query(int x) {

int res = 0;

while(x > 0) {

res += tr[x];

x -= lowbit(x);

}

return res;

}

struct YoungTable {

int a[245][MAXN];

int findA(int x,int y,int v) {

if(y == 0) return 0;

int L = 0,R = y;

while(L < R) {

int mid = (L + R + 1) >> 1;

if(a[x][mid] >= v) L = mid;

else R = mid - 1;

}

return L;

}

int findB(int x,int y,int v) {

if(y == 0) return 0;

int L = 0,R = y;

while(L < R) {

int mid = (L + R + 1) >> 1;

if(a[x][mid] < v) L = mid;

else R = mid - 1;

}

return L;

}

void Insert(int x,int y,int v,bool on) {

if(x > S) return;

y = min(a[x][0],y);

if(!on) y = findA(x,y,v);

else y = findB(x,y,v);

++y;

if(a[x][y] == 0) {

a[x][y] = v;++a[x][0];

if(on) Ins(y,1);

}

else {

int t = a[x][y];a[x][y] = v;

Insert(x + 1,y,t,on);

}

}

}A,B;

int Calc(int k) {

int res = 0;

if(k <= S) {

for(int i = 1 ; i <= k ; ++i) res += A.a[i][0];

}

else {

for(int i = 1 ; i <= S ; ++i) res += A.a[i][0];

res += Query(k) - Query(S);

}

return res;

}

void Solve() {

read(N);read(Q);

S = sqrt(N);

for(int i = 1 ; i <= N ; ++i) read(b[i]);

for(int i = 1 ; i <= Q ; ++i) {

qry[i].id = i;read(qry[i].pos);read(qry[i].k);

}

sort(qry + 1,qry + Q + 1);

int p = 1;

for(int i = 1 ; i <= N ; ++i) {

A.Insert(1,N + 1,b[i],0);B.Insert(1,N + 1,b[i],1);

while(p <= Q && qry[p].pos == i) {

ans[qry[p].id] = Calc(qry[p].k);

++p;

}

}

for(int i = 1 ; i <= Q ; ++i) {out(ans[i]);enter;}

}

int main() {

#ifdef ivorysi

freopen("f1.in","r",stdin);

#endif

Solve();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值