bzoj4094[Usaco2013 Dec]Optimal Milking最优挤奶

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4094

题目大意:

约翰有N 台挤奶机器,这些机器排成了一条直线,其中第i台机器工作效率是Ai,也就是说工作一天可以产出Ai。约翰将会开动这些机器工作Q 天。每天一早,约翰一定会维修一台机器。在第i 天早上,他维修的是第Ti 台机器,经过维修之后,这台机器的工作效率将会变成Di。在任何时候,位置相邻的两台机器都不能同时工作。请问约翰每天应该选择开动哪些机器,才能让这Q 天的总产出之和最大?

题解:

线段树

脖子上没有东西的我天真的以为直接每天取奇数位的或者偶数位的机器开,维护两个变量:和&开奇数位机器的和。但是直到我只过了两个点才发现明显的错啊qwq反例还是容易找到的[尽管我找到的反例其实并不是反例= =不管反正知道错了。

所以我的线段树后来就维护了四个变量,左端点取右端点不取,左端点不取右端点取,两个端点都取/不取的最大值。

方便不打错就直接开了个二维小数组~

方便记答案还多开了个变量mx,就是上面那四个东西中的最大值。。

好像知道怎么去维护就应该会做了吧嗯。

想了还不会再看看我那长得丑的代码↓

([0][0]就表示左右端点都不取..依此类推)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
#define maxn 201000

struct tree
{
	int l,r,lc,rc;LL a[2][2],mx;
}tr[maxn];int tlen;
LL mymax(LL x,LL y){return (x>y)?x:y;}
void bt(int l,int r)
{
	tlen++;int now=tlen;
	tr[now].l=l;tr[now].r=r;
	tr[now].lc=tr[now].rc=-1;
	memset(tr[now].a,0,sizeof(tr[now].a));
	tr[now].mx=0;
	if (l<r)
	{
		int mid=(l+r)>>1;
		tr[now].lc=tlen+1;bt(l,mid);
		tr[now].rc=tlen+1;bt(mid+1,r);
	}
}
void updata(int now,int lc,int rc)//四种情况分别更新
{
	tr[now].a[0][0]=mymax(tr[lc].a[0][1]+tr[rc].a[0][0],tr[lc].a[0][0]+tr[rc].a[1][0]);
	tr[now].a[1][0]=mymax(tr[lc].a[1][1]+tr[rc].a[0][0],tr[lc].a[1][0]+tr[rc].a[1][0]);
	tr[now].a[0][1]=mymax(tr[lc].a[0][1]+tr[rc].a[0][1],tr[lc].a[0][0]+tr[rc].a[1][1]);
	tr[now].a[1][1]=mymax(tr[lc].a[1][1]+tr[rc].a[0][1],tr[lc].a[1][0]+tr[rc].a[1][1]);	
}
void change(int now,int x,LL k)
{
	if (tr[now].l==tr[now].r)
	{
		tr[now].a[1][1]=k;
		tr[now].mx=k;
		return;
	}
	int mid=(tr[now].l+tr[now].r)>>1,lc=tr[now].lc,rc=tr[now].rc;
	if (x<=mid) change(lc,x,k);
	else change(rc,x,k);
	updata(now,lc,rc);
	tr[now].mx=mymax(tr[now].a[0][0],tr[now].a[0][1]);
	tr[now].mx=mymax(tr[now].mx,mymax(tr[now].a[1][0],tr[now].a[1][1]));
}
int main()
{
	//freopen("optmilk.in","r",stdin);
	//freopen("optmilk.out","w",stdout);
	int n,q,i;LL ans,x,c;
	scanf("%d%d",&n,&q);
	tlen=0;bt(1,n);
	for (i=1;i<=n;i++)
	{
		scanf("%lld",&x);
		change(1,i,x);
	}ans=0;
	while (q--)
	{
		scanf("%lld%lld",&x,&c);
		change(1,x,c);
		ans+=tr[1].mx;
	}printf("%lld\n",ans);
	return 0;
}


转载于:https://www.cnblogs.com/Euryale-Rose/p/6527835.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值