洛谷新春OI集训营 - 省选 Day2 T2 序列

题目链接: https://www.luogu.org/problemnew/show/P4093


佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可 。

注意:

每种变化最多只有一个值发生变化!

每种变化最多只有一个值发生变化!

每种变化最多只有一个值发生变化!

重要的事情说三遍!

输入输出格式
输入格式:
输入的第一行有两个正整数n,m,分别表示序列的长度和变化的个数。接下来一行有n个数,表示这个数列原始的状态。接下来m行,每行有2个数x,y,表示数列的第x项可以变化成y这个值。1<=x<= n。
输出格式:

输出一个整数,表示对应的答案

1<=n,m<=1e5

输入样例: 

3 4 
1 2 3 
1 2 
2 3 
2 1 
3 4
输出样例:  
3

在样例输入中,所有的变化是:

1 2 3
2 2 3
1 3 3
1 1 3
1 2 4

----------------------------------------------------------------------------------------------------------------------

很明显,虽然第i位可能会有很多种变化,但真正有用的只有原来的值a[i]、最大值maxx[i]、最小值minn[i]

此题先考虑普通动态规划,可以很容易得到状态转移方程:

设f[i]为以第i位结尾的最长长度,则有f[i]=max(f[j])+1  (j

如此,我们得到了一个O(n^2)的动规解法

但是对于1e5的数据,这显然是不能通过的

再考虑用数据结构优化DP:

对于每一个位置,把它视为坐标系上的一个点,坐标系的xy轴分别为a和maxx

那么我们对于每一个i,查找max(f[j])就变为一个二维求区间最大值问题

即:(0,0)至(minn[i],a[i])这个矩形中的点的最大值

每次求出f[i]后,再在坐标系中插入点(a[i],maxx[i])

至于二维求区间最大值问题,可以用二维线段树实现

不会二维线段树的请参考我的另一篇博客:http://blog.csdn.net/pb122401/article/details/79325126

具体代码如下:

#include
#define sz 100010
using namespace std;
long long n,m,cnt1,cnt2,id,root,ans;
long long minn[sz],maxx[sz],a[sz];
struct hh
{
    long long lson,rson,rt;//rt是这个点的内层线段树的根节点编号 
}out[sz<<1];//外层线段树,维护x坐标 
struct hhh
{
    long long lson,rson,v;//v为最大值 
}in[sz<<7];//内层线段树,维护y坐标区间最大值 
//注意:由于外层的每一个节点都对应着一棵内层线段树,in数组至少开sz<<5 
long long query_in(long long k,long long l,long long r,long long x)
{
    if (!k) return 0;//如果没有这个点答案就为0
    if (x>=r) return in[k].v;
    long long mid=(l+r)>>1;
    if (x<=mid) return query_in(in[k].lson,l,mid,x);
    return max(query_in(in[k].lson,l,mid,x),query_in(in[k].rson,mid+1,r,x)); 
}
long long query_out(long long k,long long l,long long r,long long x)
{
    if (!k) return 0;//如果没有这个点答案就为0 
    if (x>=r) return query_in(out[k].rt,1,sz,minn[id]);
    long long mid=(l+r)>>1;
    if (x<=mid) return query_out(out[k].lson,l,mid,x);
    return max(query_out(out[k].lson,l,mid,x),query_out(out[k].rson,mid+1,r,x));
}
void insert_in(long long &k,long long l,long long r,long long pos,long long v)
{
    if (!k) k=++cnt2;//动态开点,不然会MLE 
    in[k].v=max(in[k].v,v);//更新区间最大值 
    if (l==r) return;
    long long mid=(l+r)>>1; 
    if (pos<=mid) insert_in(in[k].lson,l,mid,pos,v);
    else insert_in(in[k].rson,mid+1,r,pos,v);
}
void insert_out(long long &k,long long l,long long r,long long pos,long long v)
{
    if (!k) k=++cnt1;//动态开点,不然会MLE 
    insert_in(out[k].rt,1,sz,a[id],v);//沿途每一个x区间的线段树都需要更新 
    if (l==r) return;
    long long mid=(l+r)>>1;
    if (pos<=mid) insert_out(out[k].lson,l,mid,pos,v);
    else insert_out(out[k].rson,mid+1,r,pos,v);
}
int main()
{
    scanf("%lld %lld",&n,&m);
    for (long long i=1;i<=n;i++) scanf("%lld",&a[i]),minn[i]=maxx[i]=a[i];
    for (long long i=1;i<=m;i++)
    {
        long long x,y;
        scanf("%lld %lld",&x,&y);
        minn[x]=min(minn[x],y);
        maxx[x]=max(maxx[x],y);
    }
    for (id=1;id<=n;id++)
    {
        long long v=query_out(root,1,sz,a[id])+1;//动规 
        ans=max(ans,v);
        insert_out(root,1,sz,maxx[id],v);
    }
    printf("%lld",ans);
}



你可能感兴趣的:(洛谷新春OI集训营,-,省选,题解,数据结构,树套树,线段树,Day2,T2)