bzoj1044: [HAOI2008]木棍分割 二分+dp
有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长度最大的一段长度最小. 并将结果mod 10007。
题解:先二分求最大长度的最小值,贪心的切看能不能满足条件,然后dp【i】【j】表示切了i刀,切到j的满足条件的方案数,然后复杂度是O(n*n*m),我们可以发现,转移是从一段连续的区间转移的,所以我们每次求上一层dp的前缀和来加速,复杂度就成了O(n*m),然后开二维dp会爆空间,我们需要滚动数组压缩空间
/************************************************************** Problem: 1044 User: walfy Language: C++ Result: Accepted Time:8564 ms Memory:2264 kb ****************************************************************/ //#pragma comment(linker, "/stack:200000000") //#pragma GCC optimize("Ofast,no-stack-protector") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native") //#pragma GCC optimize("unroll-loops") #include<bits/stdc++.h> #define fi first #define se second #define mp make_pair #define pb push_back #define pi acos(-1.0) #define ll long long #define vi vector<int> #define mod 10007 #define C 0.5772156649 #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 #define pil pair<int,ll> #define pli pair<ll,int> #define pii pair<int,int> #define cd complex<double> #define ull unsigned long long #define base 1000000000000000000 #define fio ios::sync_with_stdio(false);cin.tie(0) using namespace std; const double eps=1e-6; const int N=50000+10,maxn=5000+10,inf=0x3f3f3f3f,INF=0x3f3f3f3f3f3f3f3f; int ans,dp[2][N]; int a[N],sum[N],n,m,last[N]; bool ok(int x) { int now=0,te=0; for(int i=1;i<=n;i++) { if(now+a[i]<=x)now+=a[i]; else { te++; now=a[i]; if(now>x)return 0; } } if(te>m)return 0; return 1; } void prepare() { int l=0,r=sum[n]+1; while(l<r-1) { int m=(l+r)>>1; if(ok(m))r=m; else l=m; } ans=r; for(int i=1;i<=n;i++)a[i]+=a[i-1]; } void gao() { int now=0,pre=1,ans1=0; dp[now][0]=1; for(int i=1;i<=m+1;i++) { swap(now,pre); memset(dp[now],0,sizeof dp[now]); sum[0]=dp[pre][0]; for(int j=1;j<=n;j++)sum[j]=(sum[j-1]+dp[pre][j])%mod; int k=0; dp[now][0]=0; for(int j=i;j<=n;j++) { while(a[j]-a[k]>ans)k++; dp[now][j]=(sum[j-1]-(k?sum[k-1]:0))%mod; dp[now][j]=(dp[now][j]+mod)%mod; } ans1=(ans1+dp[now][n])%mod; } printf("%d %d\n",ans,(ans1+mod)%mod); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } prepare(); gao(); return 0; } /******************** 3 2 10 1 1 ********************/