降雷皇(最长上升子序列数量)解题报告

测试地址

题意简述

哈蒙有n条导线排成一排,每条导线有一个电阻值,神奇的电光只能从一根导线传到电阻比它大的上面,而且必须从左边向右传导,当然导线不必是连续的。

哈蒙想知道电光最多能通过多少条导线,还想知道这样的方案有多少。

解题思路

第一问就是简单的LIS问题,即询问最长上升子序列的长度。第二问是询问最长上升子序列的数量,如果用暴力做法,只需要多开一个数组记录数量即可,时间复杂度O(N^2);实际上我们可以利用BIT来代替枚举,使得时间复杂度至O(NlogN)。

我们用一个结构体Node{v,s}来表示当最长上升子序列长度为v时,方案数是s。那么对于每一个新元素x,我们只需要找出所有 电阻值小于x的 最长上升子序列的长度v以及数量s即可(符合BIT维护前缀和功能)。

当我们求出当前元素 x 的 v(最长上升子序列长度) 和 s(方案数) 后,用来更新ans,并将其插入树状数组第x个位置。由于题目保证x小于1e5,所以树状数组开到1e5即可。

代码示例
/*代码参考:信息学奥赛一本通P263*/
#include<cstdio>
const int N = 1e5+10;
typedef long long ll;
const ll P = 123456789;
struct Node{
	ll v,s;
	Node(){
		v = 0, s = 0;
	}
}tr[N],tmp,ans;
int n,ty;
inline void updata(int x,Node y){
	/*更改电阻值为x时的长度和方案数*/ 
	while(x <= N){
		if(y.v > tr[x].v) tr[x] = y;//如果长度变长,直接更新答案 
		else if(y.v == tr[x].v) tr[x].s = (tr[x].s+y.s)%P;
		x += x&-x;
	}
}
inline Node ask(int x){
	/*返回当电阻 <= x时,最长上升子序列的长度和方案数*/
	Node res;res.s = 1;
	while(x){
		if(tr[x].v > res.v) res = tr[x];
		else if(tr[x].v == res.v) res.s = (res.s + tr[x].s)%P;
		x -= x&-x;
	}
	return res;
}
inline void ckmax(Node &x,const Node& y){
	/*更新最终答案,即最长上升子序列以及个数*/
	if(x.v < y.v) x = y;
	else if(x.v == y.v) x.s = (x.s+y.s)%P;
}

int main(){
	scanf("%d%d",&n,&ty);
	for(int i = 1,x;i <= n;i++){
		scanf("%d",&x);
		tmp = ask(x-1);//因为这里保证x不超过1e5,所以不需要离散化 
		tmp.v++;
		ckmax(ans,tmp);	//更新答案 
		updata(x,tmp);	//更新树状数组 
	}
	printf("%lld\n",ans.v);
	if(ty == 1) printf("%lld\n",ans.s);
	return 0;
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:白松林 返回首页