P1020 导弹拦截(最长不升子序列,最长上升子序列(nlogn 二分法))

题目传送门

题意: 求给定序列的最长不升子序列和最长上升子序列。

思路: 对于最长不升子序列,我们用一个数组d存,d[i]表示不升序列长为i时,最优的序列末尾。我们每次拿到一个a[i],如果比a[i]<=d[len],那么把a[i]放在d[++len]的位置,否则如果a[i]>d[len],我们就要把a[i]插入到d数组中。我们找到d数组中第一个小于a[i]的,用a[i]替换。显然,假设替换的是d[pos],那么长为pos的不升子序列应该是替换后的a[i]而不是原来的d[pos],因为a[i]>d[pos],d[pos]=a[i]的话后面可以接更多,同时仍然保证了d数组的单调性。最长上升子序列同理。

代码

#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
#define int long long
#define vi vector<int>
#define mii map<int,int>
#define pii pair<int,int>
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
return x*f;}
using namespace std;
const int N=2e5+5;
const int inf=0x7fffffff;
const int mod=1e9+7;
const double eps=1e-6;
int a[N],d[N],d2[N];
signed main()
{
    int n=0;
    while (cin >> a[++n]); n--;
    int len=1,len2=1;
    d[1]=a[1];
    d2[1]=a[1];
    for(int i=2;i<=n;i++)
    {
        if(a[i]<=d[len])
            d[++len]=a[i];
        else
        {
            int pos=upper_bound(d+1,d+len+1,a[i],greater<int>())-d;
            d[pos]=a[i];
        }
        if(a[i]>d2[len2])
        {
            d2[++len2]=a[i];
        }
        else
        {
            int pos=lower_bound(d2+1,d2+len2+1,a[i])-d2;
            d2[pos]=a[i];
        }
    }
    cout<<len<<endl<<len2;
}

原创文章 144 获赞 13 访问量 8684

猜你喜欢

转载自blog.csdn.net/Joker_He/article/details/105802541