BZOJ 4904: [Ctsc2017]最长上升子序列 Young tableau
作者:互联网
title
\(~\)
BZOJ 4904
LUOGU 3774
Description
猪小侠最近学习了最长上升子序列的相关知识。对于一个整数序列 A=(a1,a2,?,ak),定义A 的子序列为:从A 中删除若干个元素后(允许不删,也允许将所有k 个元素都删除),剩下的元素按照原来的顺序所组成的序列。如果这个子序列的元素从左到右严格递增,则称它为A 的一个上升子序列。其中包含元素数量最多的上升子序列称为A 的最长上升子序列。例如,(2,4,5,6)和 (1,4,5,6)都是(2,1,1,4,7,5,6)的最长上升子序列,长度都为 4。现在猪小侠遇到了这样一个问题:给定一个序列 Bm=(b1,b2,?,bm),设C 是 Bm的子序列,且C 的最长上升子序列的长度不超过k,则C 的长度最大能是多少?猪小侠觉得这个问题太简单了,缺乏挑战,他决定提出一个更难的问题。于是他给了你这样一个序列 B=(b1,b2,…,bn),以及若干次询问。每次询问会给定两个整数m 和k,你需要对于B 序列的前m 个元素构成的序列 Bm=(b1,b2,?,bm)和k 回答上述问题。
Input
第一行两个整数 n,q其中 n是序列B 的长度,q是询问次数。
第二行是空格隔开的 n个正整数 b1,b2,?,bn
接下来 q行,其中第 i行包含两个整数 mi,ki,表示对 m=mi,k=ki 进行询问。
N<=50000
Output
输出共 q行,按顺序每行一个整数作为回答。
Sample Input
11 6
9 6 3 1 5 12 8 4 2 2 2
5 1
7 2
9 1
9 2
11 1
11 11
Sample Output
4
6
5
8
7
11
analysis
刚开始是看着网络流标签过去的,看到多次询问就有些疑问了,网络流的复杂度不够啊!
虽然之前也曾碰到过一道网络流题目多次询问,但是那道题是可以转化的,这道题就不太行了,无法转化成一张图,所以就去翻了一下题解,看到了一个久违的名词:杨氏矩阵。
学了一下,发现是根据这个什么 \(Dilworth\) 定理,最小链覆盖=最长反链。 问题转化为求 \(k\) 个最小不上升序列能覆盖的最大数的个数。
利用杨氏矩阵,我们可以轻松得到这个值。 不过注意杨氏矩阵的插入是 \(O(n)\)的,这时候有个定理,就是把杨氏矩阵维护东西的大小比较方式改变一下,会得到置换原矩阵后的矩阵。
于是我们原矩阵只维护\(O(\sqrt n)\)行,查询前kk行时我们只查询前 \(\sqrt n\) 行,剩下的就是置换矩阵的某一列。
发现置换矩阵也只用维护前 \(\sqrt n\) 行,于是结合二分即可做到\(O(n\sqrt nlogn+qlogn)\) 的复杂度。不过不好卡,直接暴力插入杨氏矩阵也可以通过。
大部分内容来自DZYO。
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+10,maxq=2e5+10,S=233;//S=sqrt(maxn)
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
template<typename T>inline void write(T x)
{
if (!x) { putchar('0'); return ; }
if (x<0) putchar('-'), x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) putchar(ch[num--]);
}
struct Orz{int m,k,id;}o[maxq];
inline bool cmp(Orz a,Orz b)
{
return a.m<b.m;
}
int n,q;
namespace A
{
int a[S][maxn];
inline void insert(int v,int x=1,int y=n)
{
if (x>=S) return ;
y=min(y,a[x][0]);
while (y && a[x][y]<v) --y;
++y;
if (!a[x][y]) ++a[x][0],a[x][y]=v;
else insert(a[x][y],x+1,y),a[x][y]=v;
}
}
namespace B
{
int a[S][maxn],tree[maxn];
inline int lowbit(int x) { return x & -x; }
inline void add(int x) { while (x<=n) ++tree[x],x+=lowbit(x); }
inline int sum(int x) { int ans=0; while (x) ans+=tree[x],x-=lowbit(x); return ans; }
inline int ask(int l,int r) { return sum(r)-sum(l-1); }//BIT比前缀和好些?
inline void insert(int v,int x=1,int y=n)
{
if (x>=S) return ;
y=min(y,a[x][0]);
while (y && a[x][y]>=v) --y;
++y;
if (!a[x][y]) ++a[x][0],a[x][y]=v,add(y);
else insert(a[x][y],x+1,y),a[x][y]=v;
}
}
inline int query(int x)
{
int ans=0;
if (x<S)
for (int i=1; i<=x && A::a[i][0]; ++i) ans+=A::a[i][0];
else
{
for (int i=1; i<S; ++i) ans+=A::a[i][0];
ans+=B::ask(S,x);
}
return ans;
}
int b[maxn],ans[maxq];
int main()
{
read(n);read(q);
for (int i=1; i<=n; ++i) read(b[i]);
for (int i=1; i<=q; ++i) read(o[i].m),read(o[i].k),o[i].id=i;
sort(o+1,o+q+1,cmp);
for (int i=1,j=1; i<=n && j<=q; ++i)
{
A::insert(b[i]),B::insert(b[i]);
while (j<=q && o[j].m==i) ans[o[j].id]=query(o[j].k),++j;
}
for (int i=1; i<=q; ++i) write(ans[i]),puts("");
return 0;
}
标签:11,ch,矩阵,tableau,sqrt,Young,4904,杨氏,序列 来源: https://www.cnblogs.com/G-hsm/p/11321708.html