bzoj 2122: 工作评估

这是一道好题~~~~~

首先呢,我是这样找到这道题的,百度搜索bzoj分块,于是就。。。。。。

然后发现不会做,去问人,然后在别人想的时候颓废发现自己还是不会做

于是就膜题解啊,发现只有一篇。。。就是这个 点这里

于是就膜啊膜,终于大概有可能会了,为了造福后面的人(骗访问量),我决定要写一篇题解


首先,n^2的暴力是可以过的,大概我这个40s+就过了。。主要是数据太水了

但要是你不想就这么水过这题题,请忽视这个做法并往下看

#include<cstdio>
#include<cstring>
const int N=50005;
int n,m;
int d[N],L[N];
int mymin (int x,int y){return x<y?x:y;}
int mymax (int x,int y){return x>y?x:y;}
int main()
{
    scanf("%d%d",&n,&m);
    for (int u=1;u<=n;u++) scanf("%d",&d[u]);
    for (int u=1;u<=n;u++) scanf("%d",&L[u]);
    while (m--)
    {
        int l,r,x0;
        scanf("%d%d%d",&l,&r,&x0);
        int ans=x0,lalal=x0;
        for (int u=l;u<=r;u++)
        {
            lalal=mymin(lalal+d[u],L[u]);
            lalal=mymax(lalal,x0);
            ans=mymax(lalal,ans);
        }
        printf("%d\n",ans);
    }
    return 0;
}




比较好的解法是分块

记F(i,j,x0)表示以初始代价x0依次经过第i天到第j天后的价值。

记G(i,j)为F(i,j,inf),其中inf是正无穷,S(i,j)为i到j的D值之和。则有F(i,j,x0)=min( G(i,j), x0+S(i,j) )。

这个第二条性质十分地重要,需要好好理解一下

其实也很简单,就是说假如你现在给定了一个区间l,r还有x0,接着你想啊

要是在从l到r这一段路径中,被卡住了,也就是说中间有至少一个值被L所限制了,那么你这个x0其实是没有用的,就算你是inf还是x0,答案都不会变,也就是G(i,j)

要是不会呢,那么答案就是简单的x0+S(i,j)

稍微想一想都知道,如果被卡了,前者肯定是小于后者的,所以取min值就可以很好地解决这个问题


于是,对于每一个块,我们把所有他的子串都拿出来,并维护这两个值

接着我们知道,如果对于询问的l,r,如果两个子串都被包含在[l,r]中,且有G1>=G2且S1>=S2,显然第二个子串是一定不会取到的

所以我们可以先对每一个块的G和S排序,将没用的踢掉,我们就可以得到一个G单调上升,S单调下降的东西

到时候找答案的时候这个是理所当然可以二分的


那么问题就剩下如何维护块一块之间的联系了

我们可以维护多两个东西,一个是前缀的这个东西,一个是后缀的这个东西

然后大概按照类似线段树中左右儿子维护子串最大之类的合并方法来合并就好了,注意细节


代码:


#include<cstdio>
#include<cmath>
#include<vector>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=50005;
const int M=250;
int n,m;
int D[N],T[N],SD[N];
int cnt,L[N],R[N],belong[N];
typedef pair<int,int> o;
int mymin (int x,int y){return x<y?x:y;}
int mymax (int x,int y){return x>y?x:y;}
void prepare()
{
	int nn=sqrt(n);
	L[1]=1;belong[1]=1;cnt=1;
	for (int u=1;u<=n;u++)
	{
		belong[u]=cnt;
		if (u%nn==0)
		{
			R[cnt]=u;
			cnt++;
			L[cnt]=u+1;
		}
	}
	cnt=belong[n];R[cnt]=n;
	/*for (int u=1;u<=n;u++) printf("%d ",belong[u]);
	for (int u=1;u<=cnt;u++) printf("%d %d\n",L[u],R[u]);*/
}
int F[M][M][M],tcnt;
o TT[N];
vector<o> Vec[M],pre[M],suf[M];
int stack[N];
int S (int x,int y){return F[belong[x]][x-L[belong[x]]][y-L[belong[y]]];}
int G (int x,int y){return SD[y]-SD[x-1];}
void calc (vector<o> &vec)
{
	sort(TT+1,TT+1+tcnt);
	int pnt=0;
	for (int u=1;u<=tcnt;u++)
	{
		while (pnt>0&&TT[u].second>=TT[stack[pnt]].second) pnt--;
		stack[++pnt]=u;
	}
	for (int u=1;u<=pnt;u++)
		vec.push_back(TT[stack[u]]);
}
void update (int x)
{
	for (int u=L[x];u<=R[x];u++)
	{
		int x0=1<<30;
		for (int i=u;i<=R[x];i++)
			x0=mymin(x0+D[i],T[i]),F[x][u-L[x]][i-L[x]]=x0;
	}
	tcnt=0;
	for (int u=L[x];u<=R[x];u++)
		for (int i=u;i<=R[x];i++)
			TT[++tcnt]=o(S(u,i),G(u,i));
	calc(Vec[x]);
	tcnt=0;
	for (int u=L[x];u<=R[x];u++)
		TT[++tcnt]=o(S(L[x],u),G(L[x],u));
	calc(pre[x]);
	tcnt=0;
	for (int u=L[x];u<=R[x];u++)
		TT[++tcnt]=o(S(u,R[x]),G(u,R[x]));
	calc(suf[x]);
	return ;
}
void init ()
{
	for (int u=1;u<=cnt;u++)
		update(u);
}
int lalal (vector<o> vec,int x0)
{
	int l=0,r=vec.size()-1;
	while (l+1<r)
	{
		int mid=(l+r)>>1;
		if (vec[mid].first>vec[mid].second+x0)
			r=mid;
		else l=mid;
	}
	int ret=mymin(vec[l].first,vec[l].second+x0);
	if (l+1<vec.size())
		ret=mymax(ret,mymin(vec[l+1].first,vec[l+1].second+x0));
	return ret;
}
void solve (int l,int r,int x0)
{
	if (belong[l]==belong[r])
	{
		int Y=x0,ans=x0;
		for (int u=l;u<=r;u++)
			Y=mymin(mymax(Y,x0)+D[u],T[u]),ans=mymax(ans,Y);
		printf("%d\n",ans);
		return ;
	}
	int Y=x0,ans=x0,tmp;
	for (int u=l;u<=R[belong[l]];u++)
		Y=mymin(mymax(Y,x0)+D[u],T[u]),ans=mymax(ans,Y);
	Y=mymax(Y,x0);
//	printf("YES");
	for (int u=belong[l]+1;u<belong[r];u++)
	{
		tmp=lalal(pre[u],Y);
		ans=mymax(ans,tmp);
		tmp=lalal(Vec[u],x0);
		ans=mymax(ans,tmp);
		Y=mymin(S(L[u],R[u]),G(L[u],R[u])+Y);
		tmp=lalal(suf[u],x0);
		Y=mymax(Y,tmp);
		Y=mymax(Y,x0);
	}
//	printf("NO");
	for (int u=L[belong[r]];u<=r;u++)
		Y=mymin(mymax(x0,Y)+D[u],T[u]),ans=mymax(ans,Y);
	printf("%d\n",ans);
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int u=1;u<=n;u++) {scanf("%d",&D[u]);SD[u]=SD[u-1]+D[u];}
	for (int u=1;u<=n;u++) scanf("%d",&T[u]);
	prepare();
	init();
	while (m--)
	{
		int l,r,x0;
		scanf("%d%d%d",&l,&r,&x0);
		solve(l,r,x0);
	}
	return 0;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值