【[HAOI2008]木棍分割】
阿新 • • 發佈:2019-01-02
沒寫過幾道的字首和優化\(dp\)
第一問是小學生難度的二分
第二問就直接\(dp\)了
設\(dp[i][j]\)表示當前分割點在\(i\)之後,前面一共分割了\(j\)段的方案數
利用字首和單調性,通過二分預處理出每一個點往前能擴充套件到的最大位置,之後字首和優化就可以啦
但是發現這個樣子空間會炸,而這個樣子還沒有辦法滾動起來
那好辦交換一下狀態的順序就可以啦,就可以字首和/滾動陣列優化了
程式碼
#include<iostream> #include<cstring> #include<cstdio> #define re register #define maxn 50005 #define max(a,b) ((a)>(b)?(a):(b)) const int mod=10007; int a[maxn],n,m; int dp[2][maxn],pre[2][maxn]; int p[maxn],to[maxn]; int ans; inline int read() { char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar(); return x; } inline int check(int x) { int now=0,tot=0; for(re int i=1;i<=n;i++) { if(now+a[i]>x) now=a[i],tot++; else now+=a[i]; if(tot>m) return 0; } return tot<=m; } inline int find(int l,int r,int now) { int t; while(l<=r) { int mid=l+r>>1; if(p[now]-p[mid]>ans) l=mid+1; else r=mid-1,t=mid; } return t; } int l,r; int main() { n=read(),m=read(); for(re int i=1;i<=n;i++) a[i]=read(),r+=a[i],l=max(l,a[i]); for(re int i=1;i<=n;i++) p[i]=p[i-1]+a[i]; while(l<=r) { int mid=l+r>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%d ",ans); for(re int i=1;i<=n;i++) to[i]=find(0,i-1,i)+1; for(re int i=1;i<=n;i++) if(p[i]<=ans) dp[0][i]=1; for(re int i=1;i<=n;i++) pre[0][i]=pre[0][i-1]+dp[0][i]; int o=0,cnt=0; cnt=(cnt+dp[0][n])%mod; for(re int i=2;i<=m+1;i++,o^=1) { memset(dp[o^1],0,sizeof(dp[o^1])); for(re int j=1;j<=n;j++) dp[o^1][j]=(dp[o^1][j]+pre[o][j-1]-(j-2<0?0:pre[o][to[j]-2])+mod)%mod; pre[o^1][0]=0; for(re int j=1;j<=n;j++) pre[o^1][j]=(pre[o^1][j-1]+dp[o^1][j])%mod; cnt=(cnt+dp[o^1][n])%mod; } printf("%d\n",cnt); return 0; }