agc011_f Train Service Planning (数学分析 取模技巧 线段树优化DP)

这题好难啊!!!有一篇博客已经写得很详细也很清晰了,安利一下:传送门

我再加几句,是我自己理解过程中遇到的困难:

(1)逆行的车用(N−S(a,i−1)−S(q,i),N−S(a,i)−S(q,i))表示,为什么N可以调整为K的倍数。

解释:因为所有火车同时推迟t个单位时间本质上和原来一样,所以我们可以通过推迟的方法让N变为K的倍数。

(2)为什么最后答案不是S(p,n)+S(q,n)+2*S(a,n),而是S(p,n)-p[1]+S(q,n)-q[1]+2*S(a,n)。

解释:火车在最左边一个站的等待时间其实是不算到答案里的,这里只是为了方便我们找到出发时间和N的相对关系,才添加了一个p[1]和q[1],这并不算到代价里。

#include <cstdio>
#include <algorithm>
#define lf (x<<1)
#define rg (lf^1)
#define ll long long
#define rep(i,j,k) for (i=j;i<=k;i++)
#define down(i,j,k) for (i=j;i>=k;i--)
using namespace std;
const int N=1e5+5;
int n,k,i,a[N],b[N];
ll ans,sum[N],dp[N];
int cn,id,tmp[N*2],seg[N*4],L[N],R[N];
ll mo(ll x) {
	return (x%k+k)%k;
}
void pushdown(int x) {
	if (!seg[x]) return ;
	seg[lf]=seg[x]; seg[rg]=seg[x]; seg[x]=0;
}
void updata(int x,int l,int r,int L,int R,int val)
{
	if (r<L || l>R) return;
	if (L<=l && r<=R) {
		seg[x]=val; return ;
	}
	int mid=(l+r)>>1;
	pushdown(x);
	updata(lf,l,mid,L,R,val);
	updata(rg,mid+1,r,L,R,val);
}
void query(int x,int l,int r,int pos)
{
	if (seg[x]) {
		id=seg[x]; return ;
	}
	if (l==r) return ;
	int mid=(l+r)>>1;
	if (pos<=mid) query(lf,l,mid,pos);
	else query(rg,mid+1,r,pos);
}
ll ask(int x)
{
	id=0; query(1,1,cn,x);
	if (!id) return 0;
	return dp[id]+((ll)tmp[L[id]]-tmp[x]+k)%k;
}
int main()
{
	scanf("%d%d",&n,&k);
	rep(i,1,n) scanf("%d%d",&a[i],&b[i]);
	rep(i,1,n) {
		sum[i]=sum[i-1]+a[i];
		if (b[i]==1) {
			if (a[i]*2>k) { printf("-1\n"); return 0; }
			R[i]=mo(-2*sum[i]);
			L[i]=mo(-2*sum[i-1]);
		}
		else L[i]=0,R[i]=k-1;
	}
	rep(i,1,n) tmp[++cn]=L[i],tmp[++cn]=R[i];
	sort(tmp+1,tmp+1+cn);
	cn=unique(tmp+1,tmp+1+cn)-tmp-1;
	down(i,n,1)
	{
		L[i]=lower_bound(tmp+1,tmp+1+cn,L[i])-tmp;
		R[i]=lower_bound(tmp+1,tmp+1+cn,R[i])-tmp;
	//	printf("%d %d\n",L[i],R[i]);
		dp[i]=ask(L[i]);
		if (L[i]<=R[i]) {
			if (L[i]>1) updata(1,1,cn,1,L[i]-1,i);
			if (R[i]<cn) updata(1,1,cn,R[i]+1,cn,i);
		}
		else if (L[i]-R[i]>1) updata(1,1,cn,R[i]+1,L[i]-1,i);
	}
	ans=dp[1];
//	rep(i,1,n) printf("%I64d ",dp[i]); printf("\n");
	rep(i,1,cn) ans=min(ans,ask(i));
	ans+=2*sum[n];
	printf("%lld\n",ans);
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值