牛客挑战赛36 C 纸飞机(树状数组)

链接:https://ac.nowcoder.com/acm/contest/3782/C
来源:牛客网

直线上有n座山峰,第i座的高度为hi。从某座山峰上放飞一架纸飞机,它可以从左往右依次经过一系列高度严格递减的山头。
假设五座山峰的高度依次是3,4,3,2,1。从第一座山峰上放飞的纸飞机可以依次经过第一、四、五座山峰,但不能经过第二、三座山峰。
对于每座山峰,求出要经过除这座山峰外的每座山峰,至少需要放飞多少纸飞机。(每架纸飞机的起点可以不同)
输入描述:
第一行包括一个正整数n。

第二行包括n个正整数,第i个数表示第i座山峰的高度hi。

输出描述:
输出一行,包括n个用空格隔开的正整数,第i个数表示除去第i座山峰的答案。
示例1
输入
5
2 4 3 1 5

输出
2 3 3 3 2

备注:
1≤n≤106
1≤hi≤230
思路:这题其实就是最小链覆盖问题,最小链覆盖等于最长反链,于是就是求去掉i这个值以后的最长LIS的长度。以每个点i为结尾的最长LIS用树状数组很容易求,记为Max,但去点i位置的数后的最长LIS怎么求呢?我们可以判断一下每一位是不是有可能是最初的LIS上的点,如果这些点都是唯一的,不能被替换的话,那么答案就是Max-1,如果能替换就是Max;比如样例2 4 3 1 5,最长链可能是2 4 5,4是最长链上的一点,但是去掉4以后,还有3可以替换4的位置是不是?

#include
#define lowbit(i) ((i)&(-i))
using namespace std;
const int maxn=1e6+1;
int n,size,a[maxn],b[maxn],c[maxn],vis[maxn],vis2[maxn]={0},cns[maxn]={0},num[maxn],Max=0;
void add(int x,int v)
{
	while(x<=size) c[x]=max(c[x],v),x+=lowbit(x);
}
int query(int x)
{
	int res=0;
	while(x) res=max(res,c[x]),x-=lowbit(x);
	return res;
}
int main(){
   scanf("%d",&n);
   for(int i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i];
   sort(b+1,b+1+n);
    size=unique(b+1,b+1+n)-b-1;
   for(int i=1;i<=n;++i)
   a[i]=lower_bound(b+1,b+1+size,a[i])-b;//离散化 
   for(int i=1;i<=n;++i)
   num[i]=query(a[i])+1,add(a[i],num[i]),Max=max(Max,num[i]);//求到i这个位置为止的最长lIS 
   vis[Max+1]=1e9+1;
   for(int i=n;i>=1;--i)
   if(vis[num[i]+1]>=a[i])//判断它是不是最长LIS上的点 
   {
   	vis[num[i]]=max(vis[num[i]],a[i]);vis2[i]=1; 
   }
   for(int i=1;i<=n;++i)
   if(vis2[i]) cns[num[i]]++;//统计一下这个数是否有重复,有重复的话就可以替换
   for(int i=1;i<=n;++i) printf("%d ",Max-(vis2[i]&&cns[num[i]]==1)); 
}

你可能感兴趣的:(树状数组)