题意简述
哈蒙有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;
}