2018.6.2 T3 最长上升子序列(lis)cf650D

【题目背景】
小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%的做法是 n l o g n 。可以用二分也可以用树状数组,
树状数组其实是普通dp的一种优化,维护的是以第i个数结尾的最大lis,但需离散化

100%的做法分两步
第一步是求经过每个询问的节点时最长lis
这可以分成正着的最长递增子序列和反着的最长下降子序列再-1,因为当前节点重复计算了
这你可以把询问压入节点中,在算节点时顺便求,这样是 l o g n

第二个是不经过的,
这分两种情况,如果所有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不太一样
}

有谁知道最长下降子序列的缩写吗?

猜你喜欢

转载自blog.csdn.net/qq_41893580/article/details/80556604