Luogu CF261D Maxim and Increasing Subsequence【树状数组】

题目大意

给你一个长度为 n 的 B 数组,
A 表示B数组复制 t 遍后首尾相连后的数组,
求 A 的最长上升子序列 。
有 k 组询问,maxb 表示 B 数组中最大的数。

思路

首先我们可以得到:当t大于等于当前数列不同数字的个数时,答案就是不同数字的个数。

证明

假设当前不同数字的个数 s u m sum sum 3 , t = 3 ; 3,t=3; 3,t=3;
此时共有 s u m sum sum 个相同的序列,
那么我们在 x x x 个序列选择第 x x x 小的数 ( 1 < = x < = n ) (1<=x<=n) 1<=x<=n
此时一定能保证生成含有 s u m sum sum 个元素的最长上升子序列。
图例:
在这里插入图片描述


接下来处理上述不成立的情况
我们可以设一个 f [ j ] f[j] f[j] 表示当前周期的最长序列长度,
由于数据太大,所以要开滚动数组
当枚举到当前周期时,我们考虑怎样转移。
最长上升子序列的动态转移方程为
f [ i ] = m a x ( f [ j ] ) + 1 ( j < i , a [ j ] < a [ i ] ) 。 f[i]=max(f[j])+1(j<i,a[j]<a[i])。 f[i]=max(f[j])+1(j<i,a[j]<a[i])
其实这道题也是一样的,
我们找到比 a [ j ] a[j] a[j] 小的数,也就是 a [ j ] − 1 a[j]−1 a[j]1
我们就可以想到用树状数组维护。
显然,我们用树状数组维护的是最大值。

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;

int a[100010],b[100010],f[100010],tree[100010];
int k,n,maxb,t,ans;

int lowbit(int x)
{
    
    
	return x&(-x); 
}
int find(int x)
{
    
    
	int ans=0;
	for(; x; x-=lowbit(x))
	   ans=max(tree[x],ans);   //求区间最大值
	return ans;
}
void query(int x,int c)
{
    
    
	for(; x<=maxb; x+=lowbit(x))
	   tree[x]=max(tree[x],c);   //修改区间最大值
}
int main()
{
    
    
    cin>>k>>n>>maxb>>t;
    while(k--)
     {
    
    
     	int sum=0;
     	ans=0;
     	for(int i=1; i<=n; i++)
     	 {
    
    
     	 	scanf("%d",&a[i]);
     	 	if(b[a[i]]!=k+1)   //标记求sum,不用赋初值,因为每次k+1都不一样
     	 	  sum++;
     	 	b[a[i]]=k+1;
     	 }
     	if(t>=sum)  //特判
     	 {
    
    
     	 	cout<<sum<<endl;
     	 	continue;
     	 }
     	for(int i=1; i<=100007; i++)
     	   tree[i]=f[i]=0;
     	for(int i=1; i<=t; i++)
     	 for(int j=1; j<=n; j++)
     	  {
    
    
     	  	 int c=find(a[j]-1)+1;
     	  	 if(c>f[j])
			  {
    
    
			  	f[j]=c;
			  	ans=max(ans,c);
			  	query(a[j],c);
			  }
             if(ans>sum)  //不存在ans>sum的情况
               break;
     	  }
        printf("%d\n",ans);
     }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Jackma_mayichao/article/details/108082159