[NOIP1999 普及组] 导弹拦截
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
一行,若干个整数,中间由空格隔开。
输出格式
两行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
样例 #1
样例输入 #1
389 207 155 300 299 170 158 65
样例输出 #1
6
2
提示
对于前 \(50\%\) 数据(NOIP 原题数据),满足导弹的个数不超过 \(10^4\) 个。该部分数据总分共 \(100\) 分。可使用\(\mathcal O(n^2)\) 做法通过。
对于后 \(50\%\) 的数据,满足导弹的个数不超过 \(10^5\) 个。该部分数据总分也为 \(100\) 分。请使用 \(\mathcal O(n\log n)\) 做法通过。
对于全部数据,满足导弹的高度为正整数,且不超过 \(5\times 10^4\)。
此外本题开启 spj,每点两问,按问给分。
\(\text{upd 2022.8.24}\):新增加一组 Hack 数据。
第一问:最长不上升子序列
第二问:最长上升子序列
优化:树状数组/vector/线段树/二分\(\mathcal O(n\log n)\)
点击查看代码
//树状数组维持最大值
int ask(int x)
{
int ans=0;
for(;x;x-=x&-x)ans=max(ans,c[x]);
return ans;
}
void add(int x,int y)
{
for(;x<=maxx;x+=x&-x)c[x]=max(c[x],y);
}
int main()
{
while(scanf("%d",&a[++n])!=EOF);
for(int i=1;i<n;i++)maxx=max(maxx,a[i]);
int f=1,g=1;
for(int i=n-1;i>=1;i--)
{
int x=ask(a[i])+1;
add(a[i],x);
f=max(f,x);
}
memset(c,0,sizeof c);
for(int i=1;i<n;i++)
{
int x=ask(a[i]-1)+1;
add(a[i],x);
g=max(g,x);
}
cout<<f<<endl<<g;
return 0;
}