zoukankan      html  css  js  c++  java
  • dtoi4539「TJOI / HEOI2016」序列

    题意:

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

    题解:

         设Max[i]为a[i]可能变成的最大值,Min[i]为a[i]可能变成的最小值(Max[i]和Min[i]均包括a[i]本身)。那么i,j(i<j)连在一起的条件可以很容易得出:即a[i]<=Min[j]且Max[i]<=a[j]。很好理解,如果i变了,那么考虑极端情况,变成最大值也要满足条件,如果j变了,那么同样考虑极端情况,变成最小值也要满足条件。

         这样我们就可以dp了,f[i]表示以i结尾的最长合法序列,那么f[i]=f[j]+1(j满足上述条件)。这实际上是一个三维偏序,所以可以使用树套树维护,外层为a[i],内层为Max[i],使用树状数组套线段树维护即可解决问题。

    #include<cstdio>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
    const int INF=100000;
    int n,m,a[100002],Max[100002],Min[100002],rt[100002],cnt,f[100002],ans;
    typedef struct{
        int ls,rs,Max;
    }P;
    P p[10000002];
    void gengxin(int root,int begin,int end,int wz,int z){
        if (begin>wz || end<wz)return;
        if (begin==end)
        {
            p[root].Max=max(p[root].Max,z);
            return;
        }
        int mid=(begin+end)/2;
        if (wz<=mid)
        {
            if (!p[root].ls)p[root].ls=++cnt;
            gengxin(p[root].ls,begin,mid,wz,z);
        }
        else
        {
            if (!p[root].rs)p[root].rs=++cnt;
            gengxin(p[root].rs,mid+1,end,wz,z);
        }
        p[root].Max=max(p[p[root].ls].Max,p[p[root].rs].Max);
    }
    int chaxun(int root,int begin,int end,int begin2,int end2){
        if (!root || begin>end2 || end<begin2)return 0;
        if (begin>=begin2 && end<=end2)return p[root].Max;
        int mid=(begin+end)/2;
        return max(chaxun(p[root].ls,begin,mid,begin2,end2),chaxun(p[root].rs,mid+1,end,begin2,end2));
    }
    int getans(int x,int y){
        int ans=0;
        for (;x>=1;x-=(x&-x))ans=max(ans,chaxun(rt[x],1,INF,1,y));
        return ans;
    }
    void upd(int x,int y,int z){
        for (;x<=INF;x+=(x&-x))
        {
            if (!rt[x])rt[x]=++cnt;
            gengxin(rt[x],1,INF,y,z);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            Max[i]=Min[i]=a[i];
        }
        for (int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            Max[x]=max(Max[x],y);
            Min[x]=min(Min[x],y); 
        }
        for (int i=1;i<=n;i++)
        {
            f[i]=getans(Min[i],a[i])+1;
            upd(a[i],Max[i],f[i]);
            ans=max(ans,f[i]);
        }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    进程对象的属性或方法详解
    进程理论以及开启子进程的两种方式
    计算机发展史(多道技术)
    基于socketserver实现的并发(tcp和udp)
    基于udp协议的套接字及udp协议粘包问题
    模拟ssh的远程网络传输
    周考题目及答案
    c/s架构搭建
    网络编程基础
    10.16模拟赛(湖南集训)
  • 原文地址:https://www.cnblogs.com/1124828077ccj/p/12244344.html
Copyright © 2011-2022 走看看