[HAOI2008]木棍分割

這道題磨了我好久……

第一問可以二分出來,對於每個答案,貪心的分割,最後分割出來的段大於m+1的話就不行,這樣的

第二問比較麻煩

有這樣一個DP方程:

記前綴和爲s

f[i][j]=sigma(f[i-1][k])(s[j]-s[k]<=limit)

f[i][j]表示把前j根木棍分割成i段的方案數

那麼,暴力算的話複雜度是O(mn^2),TLE無疑

我一開始沒有注意到題目性質,如果單看DP方程,是想不到什麼好的方法的

經pty提醒發現,由於s這個函數是前綴和,因此k的最小值滿足單調性,即隨着j的遞增,k的最小值單調不減

然後記g[i]爲f[i]的前綴和函數,即g[i][j]=sigma(f[i][k])(k=1...j),f的值就等於兩個g相減,可以O(1)求

這樣對於每一維是線性的,複雜度優化至O(mn)

實際操作時不需要開二維,f也只能開滾動……


第一次提交:MLE,忘記開滾動……

第二次-第n次提交:wa,因爲方便取模寫了個inc函數,結果順便把前綴和也取模了……

第n次-第m次提交:wa,貪心分割的過程寫wa了……沒計算最後一段……

我就是個傻×……不是學clj裝B,是真正的那種……


//Lib
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<queue>
using namespace std;
//Macro
#define rep(i,a,b) for(int i=a,tt=b;i<=tt;++i)
#define drep(i,a,b) for(int i=a,tt=b;i>=tt;--i)
#define erep(i,e,x) for(int i=x;i;i=e[i].next)
#define irep(i,x) for(__typedef(x.begin()) i=x.begin();i!=x.end();i++)
#define read() (strtol(ipos,&ipos,10))
#define sqr(x) ((x)*(x))
#define pb push_back
#define PS system("pause");
typedef long long ll;
typedef pair<int,int> pii;
const int oo=~0U>>1;
const double inf=1e100;
const double eps=1e-6;
string name="",in=".in",out=".out";
//Var
const int mod=10007;
int n,m,ans,maxx;
int s[50008],a[50008];
int f[2][50008],g[50008],p[50008];
inline void inc(int &x,int y){x+=y;x%=mod;}
bool Check(int limit)
{
	int sum=0,cnt=0,i=1;
	rep(i,1,n)
	{
		sum+=a[i];
		if(sum>limit)cnt++,sum=a[i];
	}
	return cnt+1<=m;
}
int Calc()
{
	int l=maxx,r=s[n],mid;
	while(l<=r)
	{
		mid=l+r>>1;
		if(Check(mid))r=mid-1;
		else l=mid+1;
	}
	return l;
}
//f[i][j]表示i段j根
int Calc(int limit)
{
	int ret=0,now=0,pre,pos=0;
	f[0][0]=1;g[0]=1;
	rep(i,1,n){while(s[i]-s[pos]>limit)pos++;p[i]=pos;g[i]=1;}
	rep(i,1,m)
	{
		pre=now;now^=1;
		memset(f[now],0,sizeof f[now]);
		rep(j,1,n)
			inc(f[now][j],g[j-1]-g[p[j]]+f[pre][p[j]]);
		inc(ret,f[now][n]);g[0]=0;
		rep(j,1,n){g[j]=g[j-1]+f[now][j];}
	}
	return ret;
}
void Work()
{
	scanf("%d%d",&n,&m);m++;
	rep(i,1,n)scanf("%d",a+i),s[i]=s[i-1]+a[i],maxx=max(maxx,a[i]);
	ans=Calc();
	printf("%d %d\n",ans,Calc(ans));
}
int main()
{
//	freopen((name+in).c_str(),"r",stdin);
//	freopen((name+out).c_str(),"w",stdout);
	Work();
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章