bzoj 1044: [HAOI2008]木棍分割 二分答案+動態規劃
阿新 • • 發佈:2019-01-03
題意:有n根木棍, 第i根木棍的長度為Li,n根木棍依次連結了一起, 總共有n-1個連線處. 現在允許你最多砍斷m個連
接處, 砍完後n根木棍被分成了很多段,要求滿足總長度最大的一段長度最小, 並且輸出有多少種砍的方法使得總長
接處, 砍完後n根木棍被分成了很多段,要求滿足總長度最大的一段長度最小, 並且輸出有多少種砍的方法使得總長
度最大的一段長度最小. 並將結果mod 10007。
分析:第一問二分答案然後貪心。
第二問f[i,j]表示前j根木棍中切i刀有多少種方案。
f[i,j]=sum(f[i-1,l-1]){sum[j]-sum[l-1]<=ans}
sum[i]表示木棍的字首和。
我們可以維護一個字首和,那麼只要知道l就可以O(1)轉移。很容易想到l是單調遞增的,那麼亂搞一下就好了。
還有陣列要開滾動的。
一開始我WA的原因是取模後進行了減法沒有判斷為負數的情況。
程式碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define MOD 10007 #define N 50005 using namespace std; int f[N],sum[N],a[N],p[N]; int main() { int n,m; scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } int l=1,r=sum[n],ans; while (l<=r) { int mid=(l+r)/2,len=0,s=0,flag=0; for (int i=1;i<=n;i++) { if (a[i]>mid) { flag=1; break; } if (len+a[i]>mid) { s++; len=a[i]; } else len+=a[i]; if (s>m) { flag=1; break; } } if (!flag) { ans=mid; r=mid-1; } else l=mid+1; } for (int i=1;i<=n;i++) if (sum[i]<=ans) f[i]=1; else break; int tot=f[n]; for (int i=1;i<=m;i++) { for (int j=1;j<=n;j++) p[j]=(p[j-1]+f[j])%MOD; int l=1; memset(f,0,sizeof(f)); for (int j=i+1;j<=n;j++) { while (sum[j]-sum[l-1]>ans) l++; f[j]=(p[j-1]-p[l-2]+MOD)%MOD; } tot=(tot+f[n])%MOD; } printf("%d %d",ans,tot); return 0; }