【[TJOI2014]上升子序列】
阿新 • • 發佈:2019-01-02
這本質上是一個\(dp\)
如果沒有"兩個上升子序列相同,那麼只需要計算一次"這一個性質,那麼就很好做了,我們用\(dp[i]\)表示以\(i\)結尾的上升子序列個數,那麼就有\(dp[i]=\sum_{j=1}^{i-1}dp[j]\)
這個暴力轉移是\(O(n^2)\)的,我們這裡可以直接用樹狀陣列來優化,於是就變成了\(O(nlogn)\)
同時由於數字可能非常大,所以需要離散化
之後再來考慮一下如何去重
首先重複的情況肯定是來自於一個之前已經出現過的數,而這個出現的數又將所有之前那個點算出來的答案又都加了一遍,這樣就會有重複的了
那我們怎麼去掉這些重複的情況呢
首先直接不考慮這個再次出現的數是肯定不對的,如果這個數和它之前出現的那個位置之間有一些比這個數小的的數,那麼這些就就沒有被計入答案,於是就錯了
但是我們可以對每一個數維護一個\(lastans[i]\),表示\(i\)這個數上次被計入答案的時候\(\sum_{j=1}^{i-1}dp[j]\)是多少,之後我們還是用樹狀陣列來查詢字首和,之後我們計入答案的應該就是這次查詢出來的答案減去\(lastans\),也就是表示新增的上升子序列的個數是多少,之後我們再把這個數加入樹狀陣列
程式碼
#include<iostream> #include<cstring> #include<cstdio> #include<map> #include<algorithm> #define re register #define lowbit(x) ((x)&(-(x))) #define maxn 100005 #define LL long long #define int long long const LL mod=1e9+7; std::map<LL,LL> ma; LL c[maxn]; int n; LL a[maxn],b[maxn]; LL lastans[maxn]; int f[maxn]; inline LL read() { char c=getchar(); LL x=0,r=1; while(c<'0'||c>'9') { if(c=='-') r=-1; c=getchar(); } while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar(); return x*r; } inline void add(int x,LL v) { for(re LL i=x;i<=n;i+=lowbit(i)) c[i]=(c[i]+v)%mod; } inline LL query(LL x) { LL ans=0; for(re LL i=x;i;i-=lowbit(i)) ans=(ans+c[i])%mod; return ans; } signed main() { n=read(); for(re int i=1;i<=n;i++) a[i]=b[i]=read(); std::sort(b+1,b+n+1); int tot=std::unique(b+1,b+1+n)-b-1; for(re int i=1;i<=tot;i++) ma[b[i]]=i; LL cnt=0; for(re int i=1;i<=n;i++) { int j=ma[a[i]]; if(!f[j]) { LL mid=query(j-1); cnt=(cnt+mid)%mod; add(j,mid+1); lastans[j]=mid; f[j]=1; continue; } LL mid=query(j-1); cnt=(cnt+mid-lastans[j]+mod)%mod; add(j,(mid-lastans[j]+2*mod)%mod); lastans[j]=mid; } std::cout<<cnt; return 0; }