1. 程式人生 > >P3970 [TJOI2014]上升子序列

P3970 [TJOI2014]上升子序列

ring can lin pac int += erl 去重 tar

傳送門

DP

十分顯然的DP,但是不好寫

設 f[ i ] 表示以第 i 個數作結尾時的方案數,原序列為 a

如果不考慮相同的序列:

  那麽轉移就是 Σ f[ j ] (0< j < i && a [ j ] < a [ i ])

  復雜度為 O(n^2)

  考慮優化:

    先去重 ,得到數組 b

    每次把f [ i ] 加到樹狀數組裏 a [ i ]的值 在 b 中的位置 的位置

    那麽 f [ i ] 就等於 query(a [ i ] 的值在 b 中的位置-1) (query為樹狀數組的詢問操作)

    (上兩行很重要,自己在腦子裏想象一下,一定要理解原因)

然後考慮去掉相同的序列

很簡單

只要每次更新完 f [ i ] 時把 f [ i ] 減去前面 a 中所有值為 a[ i ] 的位置(設為 j)

的 f[ j ]的和(還是要在腦子裏想象一下...或者看代碼來理解...

最後註意要減去長度為 1 的方案數以及一些細節

代碼其實不長

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int N=1e5+7; const int mo=1e9+7; int n,a[N],f[N],b[N],t[N],las[N],m,ans; //t是樹狀數組的數組,las[i]是前面a中所有值為a[i]的位置(設為j)的f[j]的和 inline int query(int x) { int res=0; while(x) { res=(res+t[x])%mo; x-=x&-x; } return res; } inline void add(int x,int v) {
while(x<=m) { t[x]=(t[x]+v)%mo; x+=x&-x; } } int main() { cin>>n; for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+n+1); m=unique(b+1,b+n+1)-b-1;//去重 for(int i=1;i<=n;i++) { int k=lower_bound(b+1,b+m+1,a[i])-b;//找到a[i]在b中的位置 f[i]=(f[i]+query(k-1)+1)%mo; f[i]-=las[k]; if(f[i]<0) f[i]+=mo; ans=(ans+f[i])%mo; add(k,f[i]); las[k]=(las[k]+f[i])%mo; } ans-=m; if(ans<0) ans+=mo;//減去長度為1的方案數 cout<<ans; return 0; }

P3970 [TJOI2014]上升子序列