题意:
有一个起初是空的序列。我们开始在序列中添加从1到N的数字,每次我们只在特定位置向序列添加一个数字。现在,我们想知道每次添加后LIS(最长上升子序列)的长度。
题解:
一个神奇的LIS题,题解见附件
#pragma GCC optimize (4) #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=100000+10; #define min(x,y) ((x)<(y)?(x):(y)) #define max(x,y) ((x)>(y)?(x):(y)) namespace GET{ char ch; inline char read(){ #define MAX 10000000 static char buf[MAX],*s,*t; if(s==t){ t=(s=buf)+fread(buf,1,MAX,stdin); if(s==t)return EOF; } return *s++; } // #define getchar read template <typename T> inline void Read(T &x){ while(ch<'0'||ch>'9')ch=getchar(); x=ch-'0';ch=getchar(); while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^'0'); ch=getchar(); } } inline void Write(int x){ if(x>10)Write(x/10); putchar(x%10+'0'); } } using namespace GET; struct Tree{ int left,right; int sum; }a[maxn<<2]; int n,ans; int A[maxn],Low[maxn],val[maxn]; void Init(); void LIS(); void Pushup(int); void Build(int,int,int); void Update(int,int,int); signed main(){ // freopen("in.cpp","r",stdin); Init(); return 0; } void Init(){ Read(n); Build(1,1,n); for(int i=1;i<=n;i++)Read(A[i]); for(int i=n;i>=1;i--)Update(1,i,A[i]+1); LIS(); } void LIS(){ Low[1]=val[1];ans=1;//val记录最终的序列 printf("%d ",ans ); for(int i=2;i<=n;i++){//接下来的就是二分求LIS int x=lower_bound(Low+1,Low+1+ans,val[i])-Low; ans=max(x,ans); Low[x]=val[i]; printf("%d ",ans); } } void Pushup(int u){ a[u].sum=a[u<<1].sum+a[u<<1|1].sum; } void Build(int u,int left,int right){ a[u].left=left;a[u].right=right; if(left==right){ a[u].sum=1;return; } int mid=(left+right)>>1; Build(u<<1,left,mid); Build(u<<1|1,mid+1,right); Pushup(u); } void Update(int u,int key,int p){ if(a[u].left==a[u].right){ val[key]=a[u].left; a[u].sum=0; return; } a[u].sum--; int mid=(a[u].left+a[u].right)>>1; if(a[u<<1].sum>=p)Update(u<<1,key,p); else Update(u<<1|1,key,p-a[u<<1].sum); }