1. 程式人生 > >[bzoj1044] [HAOI2008]木棍分割

[bzoj1044] [HAOI2008]木棍分割

++ 接下來 con 優化 const utc bzoj ems haoi2008

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,1000),1<=Li<=1000.

Output

  輸出有2個數, 第一個數是總長度最大的一段的長度最小值, 第二個數是有多少種砍的方法使得滿足條件.

Sample Input

3 2
1 
1
10

Sample Output

10 2

Solution

第一問二分+貪心,第二問直接前綴和優化dp就好了,dp要滾動一維。

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 2e5+10;
const int mod = 10007;

int n,m,a[maxn],q[maxn],f[2][maxn],ans,sum[maxn],r[maxn],w[maxn];

int check(int x) {
    int res=0,tmp=0;
    for(int i=1;i<=n;i++) {
        res+=a[i];
        if(a[i]>x) return 0;
        if(res>x) res=a[i],tmp++;
    }return tmp<=m;
}

void solve1() {
    int l=0,R=sum[n],mid;
    while(l<=R) if(check(mid=((l+R)>>1))) R=mid-1,ans=mid;else l=mid+1;
    printf("%d ",ans);
}

void solve2() {
    for(int i=1;i<=n;i++) if(sum[i]<=ans) f[0][i]=1;else break;
    for(int i=1,lst=0;i<=n;i++) {while(sum[i]-sum[lst]>ans) lst++;r[i]=lst;}
    int res=f[0][n];
    for(int t=1;t<=m;t++) {
        int i=t&1;memset(f[i],0,(n+4)*4);
        for(int j=1;j<=n;j++) w[j]=(w[j-1]+f[i^1][j])%mod;
        for(int j=1;j<=n;j++) f[i][j]=(w[j-1]-w[max(r[j]-1,0)])%mod;
        res=(res+f[i][n])%mod;
    }write((res+mod)%mod);
}

int main() {
    read(n),read(m);
    for(int i=1;i<=n;i++) read(a[i]),sum[i]=sum[i-1]+a[i];
    solve1();solve2();
    return 0;
}

[bzoj1044] [HAOI2008]木棍分割