随笔 - 58,  文章 - 0,  评论 - 4,  阅读 - 3296

一、题目描述

  给你一个长度为 n 的序列 a1an0ai1×109

  现在有 m 次操作,第 i 次操作将位置 pi 的数变为 vi1vi1×109

  操作仅对本次有效,并不会真的改变序列。求出每次操作后的最长严格上升子序列。

  数据范围:1n,m4×105;


 二、解题思路:

  其实这个题就是想让你掌握树状数组求 LIS 的方法。

  也许你会说:这不是小学生都会!是的,我就不会。

  实际上真的很简单,考虑到转移方程 fi=maxj=1i1fj+1[aj<ai]

  很明显的线段树区间修改不就行了吗。

  大概是我学 LIS 的时候还太年轻,什么都想不到。

  现在看来很显然,因为区间是前后缀所以可以用树状数组代替线段树。

  然后就容易求出以 ai 结尾的 LIS 长度 fi,以 ai 开头的 LIS 长度 gi

  求 gi 的时候将 ai  视为 totai+1 倒着做就行了,tot 为总的不同元素数量。

  不难发现一次修改后新的的 LIS 长度范围在原来的 [LIS1,LIS+1]

  如果 +1 当前位置 i 一定满足 fi+gi1=LIS+1

  这里的 LIS 指原来的 LISfi,gi 为修改后的 fi,gi

  离线下来全部跑一边就可以求出新的 fi,gi

  如果 1 ,则新的 fi+gi1=LIS1,且位置 i 为原来 LIS 必经的点。

  怎么求某一个点是不是 LIS 必经的点呢?这大概是这个题唯一的难点了。

  不过我当然是想不到的,直接上经典结论:

   i  LIS  fi i  LIS  gi

   fi+gi1=ans i  LIS  i  LIS  fi ()

   i  LIS 

  fi+gi1=ans  LIS  ji  fj=fi

  现在你按照上述结论跑就可以了,结论很好,也很简单,时间复杂度 O(nlog2n)


 三、完整代码

复制代码
 1 #include<iostream>
 2 #include<algorithm>
 3 #define N 800010
 4 #define rep(i,l,r) for(int i=l;i<=r;i++)
 5 #define per(i,r,l) for(int i=r;i>=l;i--)
 6 using namespace std;
 7 int n,m,num,lis,a[N],f[N],g[N],b[N],c[N],t[N],k[N],ans[N];
 8 int qry(int u){
 9     int res=0; while(u)res=max(res,c[u]),u-=u&-u; return res;
10 }
11 void upt(int u,int val){
12     while(u<=num) c[u]=max(c[u],val),u+=u&-u;
13 }
14 struct Query{
15     int pos,val,f,g,id;
16     bool operator < (const Query &t) const {
17         return pos<t.pos;
18     }
19 }q[N];
20 signed main(){
21     ios::sync_with_stdio(false);
22     cin.tie(0); cout.tie(0);
23     cin>>n>>m; rep(i,1,n) cin>>a[i],b[++num]=a[i];
24     rep(i,1,m) cin>>q[i].pos>>q[i].val,q[i].id=i;
25     rep(i,1,m) b[++num]=q[i].val;
26     sort(b+1,b+1+num); num=unique(b+1,b+1+num)-b-1;
27     rep(i,1,m) q[i].val=lower_bound(b+1,b+1+num,q[i].val)-b;
28     rep(i,1,n) a[i]=lower_bound(b+1,b+1+num,a[i])-b;
29     rep(i,1,n) f[i]=qry(a[i])+1,upt(a[i]+1,f[i]);
30     rep(i,1,num) c[i]=0;
31     per(i,n,1) g[i]=qry(num+1-a[i])+1,upt(num+2-a[i],g[i]);
32     rep(i,1,n) lis=max(lis,f[i]+g[i]-1);
33     rep(i,1,n) if(f[i]+g[i]-1==lis) t[f[i]]++;
34     rep(i,1,n) if(f[i]+g[i]-1==lis&&t[f[i]]==1) k[i]=1;
35     sort(q+1,q+1+m); rep(i,1,num) c[i]=0; int now=1;
36     rep(i,1,n){
37         while(now<=m&&q[now].pos==i)
38             q[now].f=qry(q[now].val)+1,now++;
39         upt(a[i]+1,qry(a[i])+1);
40     }
41     rep(i,1,num) c[i]=0; now=m;
42     per(i,n,1){
43         while(now>=1&&q[now].pos==i)
44             q[now].g=qry(num-q[now].val+1)+1,now--;
45         upt(num+2-a[i],qry(num+1-a[i])+1);
46     }
47     rep(i,1,m){
48         int res=q[i].f+q[i].g-1;
49         if(res>=lis) ans[q[i].id]=res;
50         else ans[q[i].id]=lis-k[q[i].pos];
51     }
52     rep(i,1,m) cout<<ans[i]<<'\n';
53     return 0;
54 }
复制代码

四、写题心得:

  对 LIS 又有了新的理解,收获如下:

  1 LIS =>Exp++!

  2线 LIS  LIS =>Exp++!

  NOIP RP++!

posted on   trh0630  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示