zoukankan      html  css  js  c++  java
  • [loj2265]最长上升子序列

    以下内容参考2019年集训队论文《浅谈杨氏矩阵在信息学竞赛中的应用》

    1.前置知识

    杨表

    标准杨表:一张网格图,满足以下条件——

    1.设其有$m$行、第$i$行有$a_{i}$个格子(格子左对齐),则$a_{1}ge a_{2}ge ...ge a_{m}$

    2.每一个格子内有一个正整数,且每行每列都严格单调递增

    3.记$n=sum_{i=1}^{m}a_{i}$(即格子总数),所有格子内的正整数恰构成1到$n$的排列

    半标准杨表:一张网格图,满足以下条件——

    1.设其有$m$行、第$i$行有$a_{i}$个格子(格子左对齐),则$a_{1}ge a_{2}ge ...ge a_{m}$

    2.每一个格子内有一个正整数,且每行单调不下降、每列严格单调递增

    (以下杨表默认均指半标准杨表)

    记$empty$为空杨表(即$m=0$时),$S_{i,j}$为杨表$S$第$i$行第$j$列的元素

    插入和删除

    行插入:对于一个杨表$S$,将$xin Z^{+}$行插入$S$,过程如下:

    1.找到第$i$行中第一个大于等于$x$的元素,将其与$x$交换(初始$i=1$)

    2.将$i$增加1并重复第一步,直至第$i$行中没有大于等于$x$的元素,则将$x$加入到该行末尾

    并记最终得到的杨表为$Sleftarrow x$

    列插入:与行插入类似,将过程中的"行"将改为"列"即可,并记最终得到的杨表为$x ightarrow S$

    删除:对于一个杨表$S$,将第$i$行最后一个元素删除,过程如下:

    1.找到第$i-1$行中最后一个小于等于$x$的的元素,将其与$x$交换(初始$x=S_{i,a_{i}}$)

    2.将$i$减小1并重复第一步,直至$i=1$则结束

    并记最终得到的杨表为$S-i$(其中$1le ile m$)

    性质

    性质1.1:对杨表执(行/列)插入或删除操作后仍是杨表

    性质1.2:行插入和删除操作互为逆运算

    关于这一性质,更完整的描述即以下两点——

    1.假设在杨表$S$中行插入$x$后最终位于第$i$行,则$S=(Sleftarrow x)-i$

    2.假设在杨表$S$中删除第$i$行后结束时的元素为$x$,则$(S-i)leftarrow x=S$

    性质1.3:$(Sleftarrow x)^{T}=x ightarrow S^{T}$(其中$S$为标准杨表,$S^{T}$即将其转置)

    性质1.4:$(x ightarrow S)leftarrow y=x ightarrow(Sleftarrow y)$

    关于这一性质的证明,参考论文的参考文献[10]

    2.杨表和排列

    对于一个排列${p_{i}}$,构造其对应的杨表$S_{p}=((emptyleftarrow p_{1})leftarrow p_{2})leftarrow...leftarrow p_{n}$

    在构造$S_{p}$的过程中,若插入$p_{x}$后$p_{x}$位于第$i$行第$j$列,则令$Q_{p}$第$i$行第$j$列的元素为$x$

    性质2.1:$S_{p}$和$Q_{p}$均为标准杨表且两者形状相同(形状即每行元素个数相同)

    性质2.2:$S_{{p_{n},p_{n-1},...,p_{1}}}=S_{p}^{T}$

    关于这一性质的证明,根据性质1.3和1.4并归纳即可

    性质2.3:对于任意两个形状相同的标准杨表$S_{1}$和$S_{2}$,恰存在一个排列$p_{i}$使得$(S_{1},S_{2})=(S_{p},Q_{p})$

    关于这一性质的证明,根据性质1.2即可通过$(S_{1},S_{2})$构造排列$p_{i}$

    性质2.4:$p_{i}$最长${ m LIS}_{k}$子序列长度为$S_{p}$的前$k$列长度和

    (其中${ m LIS}_{k}$序列即最长上升子序列长度小于等于$k$的序列)

    关于这一性质的证明,参考论文5.1的部分

    3.杨表计数

    定义序列${a_{1},a_{2},...,a_{m}}vdash n$当且仅当$a_{1}ge a_{2}ge ...ge a_{m}$且$sum_{i=1}^{m}a_{i}=n$

    记$f_{{a_{1},a_{2},...,a_{m}}}$为第$i$行有$a_{i}$个格子的标准杨表个数,显然要有$a_{1}ge a_{2}ge ...ge a_{m}$

    性质3.1:$sum_{avdash n}f_{a}^{2}=n!$

    性质3.2:$f_{a}=frac{n!}{prod_{1le ile m,1le jle a_{i}}h_{a}(i,j)}$(其中$h_{a}(i,j)$表示$(i,j)$正右和正下方的格子数+1)

    关于这一性质的证明,参考论文6.1的部分

    类似地,记$g_{n,{a_{1},a_{2},...,a_{m}}}$为第$i$行有$a_{i}$个格子且值域为$[1,n]$的半标准杨表数

    性质3.3:$g_{n,a}=prod_{1le ile m,1le jle a_{i}}frac{n+j-i}{h_{a}(i,j)}$

    关于这一性质的证明,参考论文8.1和8.2的部分

    4.其他

    论文中还有以下几部分的内容:

    (1)杨表和矩阵(第4部分)

    (2)杨表和路径计数(6.2的部分和第7部分)

    (3)复杂的杨表计数(第9部分)

    (补充一下$q$的范围:$qle 2 imes 10^{5}$)

    关于本题,将询问离线后按照$m$从小到大排序,并将所有数依次行插入杨表

    暴力插入的复杂度为$o(nlog n)$,显然无法通过

    对于杨表$S$,仅维护$S$的前$sqrt{n}$行和$S^{T}$的前$sqrt{n}$行(也即$S$的前$sqrt{n}$列),注意到$S$中每一个格子的左上角格子必然是全的,因此这包含了$S$的所有元素(否则元素个数$>n$)

    前者单次插入复杂度即$o(sqrt{n}log n)$,但后者仍难以处理

    构造$S'$也为所有数依次行插入杨表的结果,但将比较中的"大于等于"改为"小于",那么$S'$前$k$行的长度和即为最长${ m LIS}_{k}$子序列,注意到这与$S^{T}$相同

    换言之,有$S'$和$S^{T}$形状相同,同时我们仅关心于形状而不关心于具体的数,即可以维护$S'$代替$S^{T}$,同样单次插入复杂度为$o(sqrt{n}log n)$

    关于查询,根据性质2.4即求前$k$列的长度和,注意到每次插入对杨表的形状只有一处变化,因此用树状数组维护即可,修改和询问复杂度均为$o(log n)$

    总复杂度为$o(nsqrt{n}log n+qlog n)$,可以通过

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 50005
     4 #define K 250
     5 struct Data{
     6     int l,k,id;
     7     bool operator < (const Data &a)const{
     8         return l<a.l;
     9     }
    10 }q[N<<2];
    11 vector<int>v0[K],v1[K];
    12 int n,m,k,a[N],f[N],ans[N<<2];
    13 int lowbit(int k){
    14     return (k&(-k));
    15 }
    16 void update(int k){
    17     while (k<=n){
    18         f[k]++;
    19         k+=lowbit(k);
    20     }
    21 }
    22 int query(int k){
    23     int ans=0;
    24     while (k){
    25         ans+=f[k];
    26         k-=lowbit(k);
    27     }
    28     return ans;
    29 }
    30 void add0(int x){
    31     for(int i=1;i<=k;i++){
    32         if ((!v0[i].size())||(v0[i].back()<x)){
    33             v0[i].push_back(x);
    34             if (v0[i].size()>k)update(v0[i].size());
    35             return;
    36         }
    37         swap(*lower_bound(v0[i].begin(),v0[i].end(),x),x);
    38     }
    39 }
    40 void add1(int x){
    41     for(int i=1;i<=k;i++){
    42         if ((!v1[i].size())||(v1[i].back()<=x)){
    43             v1[i].push_back(x);
    44             update(i);
    45             return;
    46         }
    47         swap(*upper_bound(v1[i].begin(),v1[i].end(),x),x);
    48     }
    49 }
    50 int main(){
    51     scanf("%d%d",&n,&m);
    52     k=(int)sqrt(n);
    53     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    54     for(int i=1;i<=m;i++){
    55         scanf("%d%d",&q[i].l,&q[i].k);
    56         q[i].id=i;
    57     }
    58     sort(q+1,q+m+1);
    59     for(int i=1,j=1;i<=n;i++){
    60         add0(a[i]),add1(-a[i]);
    61         while ((j<=m)&&(q[j].l==i)){
    62             ans[q[j].id]=query(q[j].k);
    63             j++;
    64         }
    65     }
    66     for(int i=1;i<=m;i++)printf("%d
    ",ans[i]);
    67 }
    View Code
  • 相关阅读:
    记支付宝接口对接,涉及到提取证书SN号的解决方案
    Second Level Cache for Entity Framework 6.1
    记一个dynamic的坑
    使用EntityFramwork[6.1]进行级联保存的时候出现异常
    转:Transform Web.Config when Deploying a Web Application Project
    转:程序员如何增加收入
    超实用的JavaScript技巧及最佳实践(下)
    超实用的JavaScript技巧及最佳实践(上)
    Oracle PL/SQL入门语法点
    轻量级IOC框架:Ninject (下)
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/15220156.html
Copyright © 2011-2022 走看看