• [bzoj1044] P2511 [HAOI2008]木棍分割


    https://www.luogu.com.cn/problem/P2511
    https://darkbzoj.tk/problem/1044

    dp+前缀和优化
    人傻常数大,最后一个点跑了0.8秒/jk

    第一问,直接二分,然后贪心来判断是否可行就好,记求出的答案(最大长度最小值)为 (len)
    我一菜鸡在这一问就调了好长时间

    预处理出一个 (p_i),表示的是,能与 (i) 构成一个区间的最左端的一个点是什么
    比如 (p_i=j),也就是 ([j,i]) 区间是符合要求的。([j-1,i]) 就不是了,也就是它比 (len)
    这个也可以简单的用二分求出

    然后开始dp,设 (f_{i,j}) 为前 (i) 个木棍,恰好分成 (j) (不是切 (j) 刀),有多少方案
    至于为什么是恰好这位大佬讲的挺详细了
    那么,(f_{i,j})(f_{k,j-1}) 推出,也就是考虑给已经分成 (j-1) 段的每个情况,再加上一些木棍,更具体就是:

    [f_{i,j}=sum_{k=p_i-1}^{i-1} f_{k,j-1} ]

    也就是从 (p_i-1) 开始,可以为之前的状态加上 ([p_i,i]) 的木棍,来推出当前状态,直到枚举上限,就是加区间 ([i,i]) 的木棍
    发现这样转移是 (n^3) 的,但是由于是区间求和的方式来转移,所以用一下前缀和优化
    维护一个 (sum)
    每次把 (f_{i-1,j-1}) 加入 (sum) 中,然后从 (sum) 中把所有 (f_{x,j-1},x<p_i-1) 减去
    所以当前的 (f_{i,j}) 就等于 (sum) 了,复杂度 (n^2)
    前缀和优化写的不熟,处理起边界来真的烦

    然后数组要用滚动数组,答案是 (sum_{i=1}^{len} f_{n,i})

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    #define mod 10007
    int a[50005];
    int n,m,len;
    int f[50005][2];//f[i][j] 前 i 个,分成 j 段,并符合最大长度的限制,的方案数 
    int sum[50005],p[50005];
    inline int check(int max){
    	for(reg int i=1;i<=n;i++)if(a[i]>max) return 0;
    	for(reg int num=0,now,i=1;i<=n;){
    		now=0;
    		while(now<=max&&i<=n) now+=a[i],i++;
    		if(now>max) i--;
    		if(++num>m) return 0;
    	}
    	return 1;
    }
    inline int get(){
    	reg int l=1,r=sum[n],mid,ret;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(check(mid)) r=mid-1,ret=mid;
    		else l=mid+1;
    	}
    	return ret;
    }
    inline int pre(int x){
    	reg int l=1,r=x,ret,mid;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(sum[x]-sum[mid-1]<=len) ret=mid,r=mid-1;
    		else l=mid+1;
    	}
    	return ret;
    }
    inline int dp(){
    	for(reg int i=1;i<=n;i++) p[i]=pre(i);
    	for(reg int i=1;i<=n;i++)if(sum[i]<=len) f[i][1]=1;
    	int ans=f[n][1];
    	for(reg int tail,sum,j=2;j<=m;j++){
    		tail=p[2]-1;sum=f[p[2]-1][(j-1)&1];
    		for(reg int i=2;i<=n;i++){
    			sum=(sum+f[i-1][(j-1)&1])%mod;
    			while(tail<p[i]-1) sum=(sum-f[tail++][(j-1)&1]+mod)%mod;
    			f[i][j&1]=sum;
    		}
    		ans=(ans+f[n][j&1])%mod;
    	}
    	return ans;
    }
    int main(){
    	n=read();m=read()+1;
    	for(reg int i=1;i<=n;i++) a[i]=read(),sum[i]=sum[i-1]+a[i];
    	len=get();
    	std::printf("%d ",len);
    	std::printf("%d",dp());
    	return 0;
    }
    
  • 相关阅读:
    [网络流24题]餐巾计划问题
    [网络流24题]方格取数问题
    [网络流24题]试题库问题
    [网络流24题]最长递增子序列问题
    [网络流24题]圆桌问题
    [网络流24题]魔术球问题
    51nod1462 树据结构
    51nod1053 最大M子段和 V2
    51nod1026 矩阵中不重复的元素 V2
    BZOJ3832: [Poi2014]Rally
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13068939.html
Copyright © 2020-2023  润新知