Luogu3352 「ZJOI2016」線段樹
先做一遍單調棧求出來 \(l_i\) 和 \(r_i\) 表示最大值區間
然後題目轉成在所有區間選擇方法裡面有多少是可以滿足一個 \(l_i \le j \le r_i\) 的 \(a_j\) 被更新成了 \(a_i\)
然後這裡分成兩個部分,第一個是直接區間覆蓋的,另一個是間接區間覆蓋的
就變成了一個數數題
段內要 \(dp\)
令 \(f_{p,i,j}\) 表示已經把點 \(i\) 更新成 \(a_p\) 同時進行了 \(j\) 次的方案數
然後轉移推上半天也推不出來,自閉了一中午
轉移考慮控制一段不可影響的區間然後剩下的快速冪乘上去,然後中間的部分 \(dp\)
發現內部之間是有影響的,自己並不會處理
重新定義狀態:
\(f_{x,v,l,r}\) 表示進行了 \(x\) 輪之後 \(l\le i \le r,a_i\le v\) 同時 \(a_{l-1},a_{r+1}\ge v+1\) 的方案
轉移就比較好做:
\[f_{x,v,l,r}=f_{x-1,v,l,r}g_{l,r}+\sum_{j<l} f_{x-1,v,j,r} \times(j-1)+\sum_{j>r} f_{x-1,v,l,j}\times (n-j) \]
這裡 \(g_{l,r}\) 為瞎選區間與 \([l,r]\) 無關的方案數
答案統計?(\(val_j\) 為離散化後的結果)
\[ans=\sum_{j=1}^n val_j\times(sum_{i,j}-sum_{i,j-1}) \]
\[sum_{i,j}=\sum_{i\in[l,r]} f_{j,m,l,r} \]
直接做是 \(O(n^4)\) 的
考慮優化:
這裡每次轉移的時候第一維都是固定的,所以我們把這維消掉:
\[dp_{i,l,r}=\sum_{j=1}^n f_{j,i,l,r} \]
轉移其實沒變(這裡感覺理解起來沒啥問題,但是下次再遇到還真不知道能不能用上)
然後初始化的時候改動一下就好了
#include<bits/stdc++.h> using namespace std; #define int long long #define reg register #define For(i,a,b) for(reg int i=a;i<=b;++i) #define Down(i,a,b) for(reg int i=a;i>=b;--i) namespace yspm{ inline int read() { int res=0,f=1; char k; while(!isdigit(k=getchar())) if(k=='-') f=-1; while(isdigit(k)) res=res*10+k-'0',k=getchar(); return res*f; } const int mod=1e9+7,N=410; int n,q,a[N],f[2][N][N],g[N][N],cur=1,ans[N],dp1[2][N][N],dp2[2][N][N]; inline int max(int x,int y){return x>y?x:y;} inline int min(int x,int y){return x<y?x:y;} inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;} inline int del(int x,int y){return x-y<0?x-y+mod:x-y;} inline int mul(int x,int y){return x*y-x*y/mod*mod;} inline int ksm(int x,int y) { int res=1; for(;y;y>>=1,x=mul(x,x)) if(y&1) res=mul(res,x); return res; } inline int calc(int x){return x*(x+1)/2%mod;} signed main() { n=read(); q=read(); For(i,1,n) a[i]=read(); a[0]=2e18+10,a[n+1]=2e18+10; For(l,1,n) { int mx=a[l]; For(r,l,n) { mx=max(mx,a[r]); if(mx<min(a[l-1],a[r+1])) { if(l==1&&r==n) f[cur][l][r]=mx; else f[cur][l][r]=del(mx,min(a[l-1],a[r+1])); } g[l][r]=add(calc(l-1),add(calc(n-r),calc(r-l+1))); } } For(i,1,q) { For(l,1,n) { For(r,l,n) dp1[cur][l][r]=add(mul(f[cur][l][r],l-1),dp1[cur][l-1][r]); Down(r,n,l) dp2[cur][l][r]=add(mul(f[cur][l][r],n-r),dp2[cur][l][r+1]); } cur^=1; For(l,1,n) { For(r,l,n) f[cur][l][r]=add(mul(f[cur^1][l][r],g[l][r]),add(dp1[cur^1][l-1][r],dp2[cur^1][l][r+1])); } } For(i,1,n) { int sum=0; For(j,1,i) For(k,i,n) sum=add(sum,f[cur][j][k]); printf("%lld ",sum); } puts(""); return 0; } } signed main(){return yspm::main();}