当前位置: 代码迷 >> 综合 >> vijos 难解的问题
  详细解决方案

vijos 难解的问题

热度:101   发布时间:2023-11-24 22:35:17.0
描述


在你的帮助下,蔚蓝来到了埃及.在金字塔里,蔚蓝看到了一个问题,传说,能回答出这个问题的人就能受到埃及法老的祝福,可是蔚蓝日夜奋战,还是想不出来,你能帮帮他么?(XXX: 胡扯,教主怎么可能想不出来= _ =||)(WS这人说的=。=)
问题是这样的: 
给定一个序列<a1,a2,\.\.\.,an>.求最长上升子序列(lis)p1<p2<\.\.\.<pw满足a[p1]<a[p2]<\.\.\.<a[pw]
例如65 158 170 299 300 155 207 389
LIS=<65,158,170,299,300,389>。
但是,现在还有一个附加条件:求出的最长上升子序列必须含有第K项。
比如,在上面的例子中,要求求出的最长上升子序列必须含有第6项,那么最长上升子序列就是:65 155 207 389。
格式


输入格式


第一行是用空格隔开的两个正整数N、K,含义同上所述.
第二行N个数,即给出的序列.
输出格式


仅有一个数,表示含有第K项的最长上升子序列的长度.
样例1


样例输入1


5 3
1 2 3 2 1
Copy
样例输出1


3
Copy
限制


各个测试点1s
提示


对于60%的数据,N<=10000;

对于100%的数据,1<=n<=300000 ,1<=k<=n,序列的每一个数为小于2^31-1 的非负整数.

题解:LIS二分

#include <bits/stdc++.h>
#include<iostream>
using namespace std;
const int MAXN=300005;long long dp[MAXN];
long long a[MAXN];
int binary_search(int i, int len){int left,right,mid;left=1,right=len;while(left<right){mid = left+(right-left)/2;if(dp[mid] >= a[i]) right=mid ;else left=mid+1;}return left;
}int main()
{int n,k;long long int temp;cin>>n>>k;for(int i = 1; i <= n; ++i)scanf("%d", &a[i]);dp[1] = a[1];int len = 1;for(int i = 2; i <= k; ++i) {   //以a[k]结尾的最长上升子序列if(a[i]>dp[len])dp[++len] = a[i];else {int pos = binary_search(i,len);//int pos = lower_bound(dp+1, dp + len, a[i]) -dp;  或者用库函数dp[pos] = a[i];if(i==k)     //如果不能放在最后len = pos;}}int ans = 1;dp[1] = a[k];for(int i = k+1; i <= n; ++i) {if(a[i] > dp[ans])dp[++ans] = a[i];else {int pos = binary_search(i,ans);//int pos = lower_bound(dp+1, dp + ans, a[i]) - dp; if(pos != 1)dp[pos] = a[i];}}printf("%d\n", ans + len - 1);return 0;
}