一开始连题解都看不懂,对着题解敲了一遍算是会了(
题意:给定一个序列,对于每次询问,输出把这位数改成另一个数后的LIS长度。
下面的方法是通过 离线+树状数组 的解法做的。
核心的思想是在每修改一位时,这一位前面和后面的序列是不变的,并且LIS可以拆分为以该位开头的LIS与以该位结尾的LIS,所以这一位前面的LIS长度以及这一位后面的LIS长度都可以利用起来。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read()
{
unsigned n=0; int f=1; char ch;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;'0'<=ch&&ch<='9';ch=getchar()) n=(n<<3)+(n<<1)+(ch^48);
return f*n;
}
//#define DEBUG 1
const int N=5E5+5;
int n,m;
int h[N],b[N<<1],cnt,now;
int f[N],g[N],len,lis[N];
//f[i]-以i为终点的LIS长度
//g[i]-以i为起点的LIS长度
struct ask
{
int id,x,val;
int f,g;
}qu[N];
bool cmp1(ask a,ask b){return a.x==b.x?a.id<b.id:a.x<b.x;}
int res[N];
#define x(i) qu[i].x
#define F(i) qu[i].f
#define G(i) qu[i].g
#define res(i) res[qu[i].id]
int tree[N<<1];//维护1~x的数中dp值的最大值
inline int lowbit(int x){return x&-x;}
inline void update(int x,int k){for(;x<=cnt;x+=lowbit(x)) tree[x]=max(tree[x],k);}
inline int query(int x){int ans=0; for(;x;x-=lowbit(x)) ans=max(ans,tree[x]); return ans;}
inline void Solve()
{
#ifdef DEBUG
printf("Debuging...\n");
#endif
n=read(),m=read();
for(int i=1;i<=n;i++) b[i]=h[i]=read();
cnt=n;
for(int i=1;i<=m;i++)
qu[i].id=i,qu[i].x=read(),b[++cnt]=qu[i].val=read();
//离散化
sort(b+1,b+cnt+1);
cnt=unique(b+1,b+cnt+1)-b-1;
for(int i=1;i<=n;i++) h[i]=lower_bound(b+1,b+cnt+1,h[i])-b;
for(int i=1;i<=m;i++) qu[i].val=lower_bound(b+1,b+cnt+1,qu[i].val)-b;
//dp处理f,g
//f-> 1->i的最长上升子序列
memset(tree,0,sizeof(tree));
for(int i=1;i<=n;i++) f[i]=query(h[i]-1)+1,update(h[i],f[i]);
//g-> n->i的最长下降子序列
//我们将求h[n->i]的最长下降子序列,转化为求cnt-h[n->i]+1的最长上升子序列
memset(tree,0,sizeof(tree));
for(int i=n;i>=1;i--) g[i]=query(cnt-h[i])+1,update(cnt-h[i]+1,g[i]);
//求LIS的长度len,并找出LIS某一位中的可能的数的数量
//过i点的LIS长度为 f[i]+g[i]-1
for(int i=1;i<=n;i++) len=max(len,f[i]+g[i]-1);
for(int i=1;i<=n;i++) if(f[i]+g[i]-1==len) ++lis[f[i]];//这位数可以在LIS的f[i]位,记录
//离线处理询问
sort(qu+1,qu+m+1,cmp1);//按x排序
//处理出改变第i位时的f[i]
//now-当前处理到的位的前一位
//now每推进一位就把该位的f[i]加入树状数组
//这样查询出的F(i)就是以i-1位结尾的LIS长度
memset(tree,0,sizeof(tree)),now=1;
for(int i=1;i<=m;i++)
{
for(; now < qu[i].x ; update(h[now],f[now]),++now);
F(i)=query(qu[i].val-1);
}
//处理出改变第i位时的g[i],与上面同理
memset(tree,0,sizeof(tree)),now=n;
for(int i=m;i>=1;i--)
{
for(; now > qu[i].x ; update(cnt-h[now]+1,g[now]),--now);
G(i)=query(cnt-qu[i].val);
if(F(i)+G(i)+1>len) res(i)=F(i)+G(i)+1;
}
//根据F(i)和G(i)情况分类讨论
//i点修改后过i点的为 F(i)+G(i)+1
//如果修改后过i点的LIS长度短于len,并且这个点是LIS该位上的唯一点,那么LIS只能将该位剔除,答案为len-1
//否则取修前与修后LIS长度的最大值(增长的才能让答案更新)
for(int i=1;i<=m;i++)
if(!res(i))
{
if( F(i)+G(i)+1<len && f[x(i)]+g[x(i)]-1==len && lis[f[x(i)]]==1) res(i)=len-1;
else res(i)=max(F(i)+G(i)+1,len);
}
//输出结果
for(int i=1;i<=m;i++) printf("%d\n",res[i]);
return ;
}
//#define FILES_IO 2
int main()
{
#ifdef FILES_IO
string FileName="test",ReadFile=FileName+".in",WriteFile=FileName+".out";
freopen(ReadFile.c_str(),"r", stdin);
freopen(WriteFile.c_str(),"w", stdout);
#endif
//srand((unsigned)time(0));
//ios::sync_with_stdio(false);
//cin.tie(0);
int T=1;
//scanf("%d",&T);
for(;T--;)
Solve();
#ifdef FILES_IO
fclose(stdin);
fclose(stdout);
#endif
return 0;
}