1044: [HAOI2008]木棍分割
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3291 Solved: 1238
[Submit][Status][Discuss]
Description
有n根木棍, 第i根木棍的長度爲Li,n根木棍依次連結了一起, 總共有n-1個連接處. 現在允許你最多砍斷m個連
接處, 砍完後n根木棍被分成了很多段,要求滿足總長度最大的一段長度最小, 並且輸出有多少種砍的方法使得總長
度最大的一段長度最小. 並將結果mod 10007。。。
Input
輸入文件第一行有2個數n,m.接下來n行每行一個正整數Li,表示第i根木棍的長度.n<=50000,0<=m<=min(n-1,10
00),1<=Li<=1000.
Output
輸出有2個數, 第一個數是總長度最大的一段的長度最小值, 第二個數是有多少種砍的方法使得滿足條件.
Sample Input
1
1
10
Sample Output
HINT
兩種砍的方法: (1)(1)(10)和(1 1)(10)
相信O(nm)...
前綴和優化DP,用單調隊列維護.
#pragma GCC optimize(2)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxm 1010
#define maxn 50100
#define mod 10007
using namespace std;
int dp[maxn],q[maxn],pos[maxn],ans;
int a[maxn],s[maxn],ql,qr,sum[maxn],p[maxn],n,m,l,r;
bool check(int ans){
int p=0,len=0;
for(int i=1;i<=n;++i){
if(len+a[i]>ans)p++,len=a[i];
else len+=a[i];
}
return p<=m;
}
int inline update(int x){
return x>mod?x-mod:x;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
s[i]=a[i]+s[i-1];
l=max(l,a[i]),r+=a[i];
}
while(l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%d ",l);
for(int i=1;i<=n;++i)sum[i]=1;
memset(p,-1,sizeof(p));
ql=qr=0,q[qr++]=0;
for(int j=1;j<=n;++j){
while(ql<qr&&s[j-1]-s[q[ql]]>l)ql++;
if(ql<qr)p[j]=q[ql];
q[qr++]=j;
}
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j)
if(p[j]!=-1)dp[j]=update(sum[j-1]-sum[p[j]]+mod);
for(int j=1;j<=n;++j)sum[j]=update(sum[j-1]+dp[j]);
for(int j=n;j>=1&&s[n]-s[j-1]<=l;--j)ans=update(ans+dp[j]);
}
printf("%d",(ans+mod)%mod);
}