标签:c++ 表示 相同 子序列 lin 递增 log 题解 树状
数组A包含N个整数(可能包含相同的值)。设S为A的子序列且S中的元素是递增的,则S为A的递增子序列。如果S的长度是所有递增子序列中最长的,则称S为A的最长递增子序列(LIS)。A的LIS可能有很多个。例如A为:{1 3 2 0 4},1 3 4,1 2 4均为A的LIS。给出数组A,求A的LIS有多少个。由于数量很大,输出Mod 1000000007的结果即可。相同的数字在不同的位置,算作不同的,例如 {1 1 2} 答案为2。
1 <= N <= 50000,0 <= Ai <= 10^9
记得以前在光华好像有一道题就是算这个,当时就只想着在dp里动手脚,但是正确性不能保证。现在也没啥新想法TAT
一搜题解,啥,CDQ啊??奇怪的姿势。最后找到了一篇姿势优美的题解,但是思路只有一行。又只有从代码推思路。
回想n2的dp的做法,f[i]表示以a[i]为结尾的最长上升子序列的长度,我们应该应该找一个在最大的f[j]中找尽量小的j,使j<i并且a[j]<a[i],即可使f[i]=f[j]+1.
就是找j的这一步,用树状数组优化为logn。
用树状数组同时维护len和num,表示长度和数量。每次询问a[i]之前树状数组上的的len和num来更新f[i]。只要每一次长度被更新,数量也同步更新。
注意到a[i]太大了,应该离散化一下才能用树状数组做。
#include<bits/stdc++.h> using namespace std; #define N 50050 #define mod 1000000007 int n,cnt,a[N],tmp[N]; struct email { int len,num; }ans,f[N],t[N]; inline int lowbit(int x){return x&(-x);} inline void dp(email &x,email y) { if(x.len>y.len)return; if(x.len<y.len)x=y; else x.num=(x.num+y.num)%mod; } inline email query(int x) { email ret;ret.len=0,ret.num=1; for(;x;x-=lowbit(x)) dp(ret,t[x]); return ret; } inline email add(int x,email y) { for(;x<=cnt;x+=lowbit(x)) dp(t[x],y); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]),tmp[i]=a[i]; sort(tmp+1,tmp+1+n); cnt=unique(tmp+1,tmp+1+n)-(tmp+1); for(int i=1;i<=n;i++)a[i]=lower_bound(tmp+1,tmp+1+cnt,a[i])-tmp; for(int i=1;i<=n;i++)f[i]=query(a[i]-1),f[i].len++,add(a[i],f[i]); for(int i=1;i<=n;i++)dp(ans,f[i]); printf("%d\n",ans.num); }
标签:c++ 表示 相同 子序列 lin 递增 log 题解 树状
原文地址:https://www.cnblogs.com/NSD-email0820/p/9803435.html