【题目背景】
小G同学有一个序列叫法法。
小 S想要从小 G同学的序列中选出满足他的条件的尽量多的元
素。
【题目描述】
给定一个长度为 n的数列。求出这个序列的 LIS。
再给定 m个询问(a,b)。表示原数组的第 a个数改成 b之后。
求出数组的最长上升子序列(LIS)的长度。
询问之间相互独立。
Hint: LIS 指的是在一个数组中,选出一个最长的子序列 A,
设其长度为 L,满足对于所有 i∈[2,L] A[i‐1] < A[i],即严
格上升。
【输入格式】
第一行一个数 n。
接下来一行 n个数字,表示原数组 w[i]。
接下来一行一个数 m,表示操作个数。
接下来 m行,一行 2个数,为[a,b]表示一次操作。
【输出格式】
由于输出太大,设 ans[i]为第 i次询问的答案乘上 i,ans[0]
为初始序列的 LIS,你只要输出 ans[0]到 ans[m]的异或和。
【样例输入1】
4 4
1 2 3 4
1 1
1 4
4 3
4 5
【样例输出1】
31
【样例 1解释】
一开始的 LIS为[1,2,3,4],为 4,ans[0] = 4
第一次询问序列为修改和原数组相同,ans[1] = 4 * 1 = 4
第二次询问序列修改后为[4,2,3,4],LIS为[2,3,4],LIS长
度为 3,ans[2] = 3 * 2 = 6
第三次询问序列修改后为[1,2,3,3],LIS为[1,2,3],3可以
使第三个 3也可以是第四个 3,LIS长度为 3,ans[3] = 3 * 3 = 9
同样的道理,我们可以得到第四次询问的 LIS为 4,ans[4] = 4
* 4 = 16
输出答案为 4 xor 4 xor 6 xor 9 xor 16 = 31
对于所有数据,满足 1 <= a <= n
一开始觉得有点人类智慧,其实是被网上巨长题解吓到了
50%的做法是
。可以用二分也可以用树状数组,
树状数组其实是普通dp的一种优化,维护的是以第i个数结尾的最大lis,但需离散化
100%的做法分两步
第一步是求经过每个询问的节点时最长lis
这可以分成正着的最长递增子序列和反着的最长下降子序列再-1,因为当前节点重复计算了
这你可以把询问压入节点中,在算节点时顺便求,这样是
第二个是不经过的,
这分两种情况,如果所有lis经过此节点,ans=lis-1;不然就是lis
根据之前的理解,每个lis是由在它前面且比他小的转移过来的,如果它不是唯一的,lis就不会减
所以判断原序列中lis相同的是否只有一个即可
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
#define dd c=getchar()
inline int read() {int s=0,w=1;char c;while (dd,c>'9' || c<'0') if (c=='-') w=-1;while (c>='0' && c<='9') s=s*10+c-'0',dd;return w*s;}
#undef dd
inline void write(int x) {
if (x<0) putchar('-'),x=-x;if (x>=10) write(x/10);putchar(x%10+'0');
}
inline void writeln(int x) {
write(x);puts("");
}
const int N=400007;
struct query {
int v,id;
};
vector<query> v[N];
int a[N],tr[N<<2],che[N],ok[N],w[N<<2],alis[N],blis[N],lisa[N],lisb[N],ans[N];
int n,m,cnt,tlis;
int lowbit(int x) {return x&-x;}
void add(int x,int v) {
for (;x<=cnt;x+=lowbit(x)) tr[x]=max(tr[x],v);
}
int find(int x) {
int sum=0;
for (;x;x-=lowbit(x)) sum=max(tr[x],sum);
return sum;
}
signed main() {
n=read(),m=read();
for (int i=1;i<=n;i++) w[++cnt]=a[i]=read();
for (int i=1,pos,val;i<=m;i++) {
pos=read();val=read();
w[++cnt]=val;
v[pos].push_back((query){val,i});
}
sort(w+1,w+cnt+1);
cnt=unique(w+1,w+cnt+1)-w-1;
for (int i=1;i<=n;i++) {
for (int j=0;j<v[i].size();j++)
v[i][j].v=lower_bound(w+1,w+cnt+1,v[i][j].v)-w;
a[i]=lower_bound(w+1,w+cnt+1,a[i])-w;
}
for (int i=1;i<=n;i++) {
for (int j=0;j<v[i].size();j++) {
alis[v[i][j].id]=find(v[i][j].v-1)+1;
}
lisa[i]=find(a[i]-1)+1;
tlis=max(tlis,lisa[i]);
add(a[i],lisa[i]);
}
memset(tr,0,sizeof tr);
for (int i=n;i>=1;i--) {
for (int j=0;j<v[i].size();j++) {
blis[v[i][j].id]=find(cnt-v[i][j].v)+1;
}
lisb[i]=find(cnt-a[i])+1;
add(cnt-a[i]+1,lisb[i]);
}
memset(che,-1,sizeof che);
for (int i=1;i<=n;i++) {
if (lisa[i]+lisb[i]-1==tlis) {
if (che[lisa[i]]==-1) che[lisa[i]]=i;
else che[lisa[i]]=-2;
}
}
for (int i=1;i<=tlis;i++)
if (che[i]>0) ok[che[i]]=1;
for (int i=1;i<=n;i++) {
for (int j=0;j<v[i].size();j++) {
ans[v[i][j].id]=max(ok[i]?tlis-1:tlis,alis[v[i][j].id]+blis[v[i][j].id]-1);
}
}
int res=tlis;
for (int i=1;i<=m;i++) res^=(ans[i]*i);
writeln(res);//输出跟cf不太一样
}
有谁知道最长下降子序列的缩写吗?