HAOI2008 木棍分割 二分答案 前缀和优化 单调队列 滚动数组

来源:互联网 发布:潮汕女孩知乎 编辑:程序博客网 时间:2024/05/21 14:06

NKOJ4244 HAOI2008 木棍分割

问题描述

有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长度最大的一段长度最小. 并将结果mod 10007

输入格式

第一行有2个数n,m. 接下来n行每行一个正整数Li,表示第i根木棍的长度.

输出格式

2个数, 第一个数是总长度最大的一段的长度最小值, 第二个数是有多少种砍的方法使得满足条件.

样例输入

3 2
1 1 10

样例输出

10 2

样例说明

两种砍的方法: (1)(1)(10)和(1 1)(10)

数据范围

n<=50000, 0<=m<=min(n-1,1000).
1<=Li<=1000.


这道题的综合性比较强。考察了很多知识点,尤其是对于优化的考察。但是其实并没有太复杂。

第一个问题,“长度最大的最小”,显然的二分答案标志。轻松水过。

第二个问题,考虑递推。定义状态f[i][j]表示前i根木棍切j刀,且第j刀切在i处的方案总数,那么有

f[i][j]=f[k][j1],sum[i]sum[k]Ans
Ans为上一个问题的答案。这当然是正解,然而我们注意到,时间复杂度O(mn2),空间复杂度O(mn),从哪个方面都过不了。

所以我们需要优化。滚动数组的优化是很容易想到的,因为注意到f[i][j]只与第二维是j1的状态有关系。

递推式里面有多个连续的数求和的形式,考虑前缀和优化。但是这里有个问题:如何定位k?注意到sum数组单调递增,差值小于等于一个定值,这是显然的滑动窗口模型。那么可以用单调队列优化。

优化之后,时间复杂度O(mn),空间复杂度O(2m),就能够AC了。


代码:

#include<stdio.h>using namespace std;const int mod=10007,MAXN=50005;int N,M,Ans,sum[MAXN],Max,f[MAXN],sumf[MAXN][2];int Q[MAXN],head,tail;inline int _R(){    char s=getchar();int v=0,sign=0;    while((s!='-')&&(s>57||s<48))s=getchar();    if(s=='-')sign=1,s=getchar();    for(;s>47&&s<58;s=getchar())v=v*10+s-48;    if(sign)v=-v;    return v;}bool check(int x){    int cnt=0,i,las=0;    for(i=1;i<=N;i++)    {        if(sum[i]-sum[las]>x)        {            cnt++;            las=i-1;        }        if(cnt>M)return false;    }    return true;}int main(){    int L,R=0,mid,i,j,x,y,t;    N=_R();M=_R();    for(i=1;i<=N;i++)    {        x=_R(),sum[i]=sum[i-1]+x;        Max=Max<x?x:Max;    }    L=Max;R=sum[N];    while(L<=R)    {        mid=L+R>>1;        if(check(mid))R=mid-1;        else L=mid+1;    }    printf("%d ",L);    for(i=1;i<=N;i++)    {        if(sum[i]<=L)f[i]=1;        sumf[i][1]=sumf[i-1][1]+f[i];    }    Ans+=f[N];    for(j=1;j<=M;j++)    {        head=0;        x=j&1;y=x^1;        for(i=1;i<=N;i++)        {            while(sum[i]-sum[head]>L)head++;            t=head;            if(t)t--;            f[i]=(sumf[i-1][x]-sumf[t][x])%mod;            sumf[i][y]=(sumf[i-1][y]+f[i])%mod;        }        Ans=(Ans+f[N])%mod;    }    printf("%d",(Ans+mod)%mod);}
阅读全文
0 0
原创粉丝点击