【例題 \(1\) 】 遞增子序列
\(\color{white}{link}\)
考慮 \(dp.\)
\(dp[i][j]\) 表示以元素 \(i\) 為結尾,長度為 \(k\) 的方案數。
那麼顯而易見就有一個轉移方程:
\[dp[i][j]=\sum_{a[k]<a[i] ,\ k<i} dp[k][j-1]
\]
先拋去第二維度的 \(j\) ,這是可以做一個關於 \(a[i]\) 值的大小,數值是 \(dp\) 的樹狀陣列的。
維護兩個操作:單點修改,區間查詢。
當然因為 \(a[i]\) 實在是太大了,所以離散化。
如果加上第二層維度那就建立 \(k\) 個樹狀陣列就好了。
程式
#include <bits/stdc++.h>
#define int long long
#define ls (u << 1)
#define rs (u << 1 | 1)
#define lson ls, l, mid
#define rson rs, mid + 1, r
#define mid ((l + r) >> 1)
const int inf = 1e18;
const int N = 1e4 + 10;
const int mod = 123456789;
using namespace std;
int n, m;
struct ta {
int tree[N];
inline int lowbit(int x) { return x & -x; }
inline void update(int x, int k) {
while (x <= N) (tree[x] += k) %= mod, x += lowbit(x);
}
inline int query(int x) {
int res = 0;
while (x) (res += tree[x]) %= mod, x -= lowbit(x);
return res;
}
} t[105];
int tmp, val[N], b[N];
signed main() {
while (~scanf("%lld%lld", &n, &m)) {
for (int i = 1; i <= m; ++i) memset(t[i].tree, 0, sizeof t[i].tree);
memset(val, 0, sizeof val), memset(b, 0, sizeof b);
tmp = 0;
for (int i = 1; i <= n; ++i) scanf("%lld", &val[i]), b[i] = val[i];
stable_sort(b + 1, b + n + 1);
int len = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; ++i) val[i] = lower_bound(b + 1, b + len + 1, val[i]) - b;
for (int i = 1; i <= n; ++i) {
t[1].update(val[i], 1);
for (int j = 1; j < m; ++j) {
tmp = t[j].query(val[i] - 1);
if (!tmp)
break;
t[j + 1].update(val[i], tmp);
}
}
printf("%lld\n", t[m].query(n) % mod);
}
return 0;
}
選做:
【練習 \(1\) 】 折線統計
\(\color{white}{link}\)
考慮 \(dp.\)
按 \(x\) 排序,
設 \(dp[i][j][0/1]\) 為以 \(i\) 位置結束,長度為 \(j\) 的 \(0\) 上升 \(1\) 下降的方案數。則有
\[dp[i][j][0]=\sum _{a[k]<a[i],\ k<i } dp[k][j][0] + \sum _{a[k]<a[i],\ k<i } dp[k][j-1][1]
\]
\[dp[i][j][0]=\sum _{a[k]>a[i],\ k<i } dp[k][j-1][0] + \sum _{a[k]>a[i],\ k<i } dp[k][j][1]
\]
由於我是 \(Sach\),所以我不予證明。
注意因為 \(y\) 很大所以要離散化。
最佳化使用關於 \(a[i]\) 建立維護 \(dp\) 的樹狀陣列。沒有試過線段樹。
時間複雜度分析:
\(\mathcal{O(n)O(\log \alpha)O(m)=O(nm \log n)}\) 瓶頸在計算 \(dp\) 上。
不過足以透過本題。
程式
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,mod=1e5+7;
int n,k,mx;
struct fwt{ int tree[N];
inline int lb(int x){ return x&-x; }
inline void upd(int x,int k){ while(x<=mx) (tree[x]+=k)%=mod, x+=lb(x); }
inline int q(int x){ int s=0; while(x) (s+=tree[x])%=mod, x-=lb(x); return s%=mod;}
}t1[12],t2[12];
struct pt{ int x,y; } a[N];
inline bool cmp(pt x,pt y){ return x.x<y.x; }
signed main(){
scanf("%lld%lld",&n,&k), k++;
for(int i=1;i<=n;++i) scanf("%lld%lld",&a[i].x,&a[i].y), mx=max(mx,a[i].y);
stable_sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;++i){
t1[1].upd(a[i].y,1), t2[1].upd(a[i].y,1);
for(int j=2;j<=k;++j){
int tmp1=(t1[j].q(a[i].y-1)+t2[j-1].q(a[i].y-1))%mod, tmp2=((t1[j-1].q(mx)-t1[j-1].q(a[i].y))%mod+(t2[j].q(mx)-t2[j].q(a[i].y))%mod+mod)%mod;
if(!tmp1 && !tmp2) break;
t1[j].upd(a[i].y,tmp1), t2[j].upd(a[i].y,tmp2);
}
}
return printf("%lld\n",(t1[k].q(mx)+t2[k].q(mx))%mod),0;
}