P4694 [PA2013] Raper

P4694 [PA2013] Raper

题目描述

你需要生产 k 张光盘。每张光盘都要经过两道工序:先在 A 工厂进行挤压,再送到 B 工厂涂上反光层。

你知道每天 A、B 工厂分别加工一张光盘的花费。你现在有 n 天时间,每天可以先送一张光盘到 A 工厂(或者不送),然后再送一张已经在 A 工厂加工过的光盘到 B 工厂(或者不送),每家工厂一天只能对一张光盘进行操作,同一张光盘在一天内生产出来是允许的。我们假定将未加工的或半成品的光盘保存起来不需要费用。

求生产出 k 张光盘的最小花费。

输入格式

第一行包含两个整数 n,k,表示有 n 天,要生产 k 张光盘。

第二行包含 n 个整数,第 i 个整数表示第 i 天送到 A 工厂加工光盘的花费。

第三行包含 n 个整数,第 i 个整数表示第 i 天送到 B 工厂加工光盘的花费。

提示

保证 1kn5×105, 1ai,bi109

Solution:

拿到题目第一眼:这不费用流秒了?
看完数据范围:被秒的是我

费用流的做法很简单:

st ed w flow
S i 0 k
i i+1 0 inf
i' i'+1 0 inf
i i' a[i] 1
i' T b[i] 1

正解:线段树模拟费用流

我们先思考一下 a,b 操作是不是很像 () 我们将 a 当做 ( , b 当做 ). 那么我们最终的答案会是一个匹配的括号序列。那么我们添加括号的方式就有两种:..(...).. 和 ..)...(..

对于():
我们显然可以在任意合法的a,b对上添加()

但是对于 )( :
我们设 a = ( = 1 ,b = ) = -1 缀和数组S
假设我们要将 a,b 分别放在 L,R 我们需要保证前 在区间 [L,R) 上的 S 恒大于0

但是我们发现维护区间最小值是否大于0貌似有点不好整(题解说的),所以我们只需在两个区间合并时判断两个区间的最小值的大小关系然后合并就好了

具体细节见代码

Code:

#include<bits/stdc++.h>
#define int long long
const int N=5e5+5;
const int inf=1e9;
using namespace std;
int n,m;
int a[N],b[N];
long long ans;
struct Node{
int x,y;
};
inline bool operator <(Node x,Node y)
{
return a[x.x]+b[x.y] < a[y.x]+b[y.y];
}
struct Tree{
int ma,mb,la,lb,mn,tag;
Node va,vb,vc;
};
Tree _new(int pos)
{
return Tree{pos,pos,pos,0,0,0,Node{pos,pos},Node{0,0},Node{pos,pos}};
}
inline Tree operator +(Tree x,Tree y)
{
Tree z=_new(0);
z.ma= a[x.ma] < a[y.ma] ? x.ma : y.ma;
z.mb= b[x.mb] < b[y.mb] ? x.mb : y.mb;
z.mn= min(x.mn,y.mn);
z.va= min(Node{x.ma,y.mb},min(x.va,y.va));
z.vb= min(x.vb,y.vb);
z.vc= min(Node{y.ma,x.mb},min(x.vc,y.vc));
if(x.mn<y.mn)
{
z.vb=min(z.vb,min(Node{y.ma,x.lb},y.vc));
z.la=x.la;
z.lb=(b[x.lb] < b[y.mb] ? x.lb :y.mb);
//co<<"case 2: ";
}
if(x.mn>y.mn)
{
z.vb=min(z.vb,min(Node{y.la,x.mb},x.vc));
z.la=(a[x.ma] < a[y.la] ? x.ma : y.la);
z.lb=y.lb;
//co<<"case 1: ";
}
if(x.mn==y.mn)
{
z.vb=min(z.vb,Node{y.la,x.lb});
z.la=x.la;
z.lb=y.lb;
//co<<"case 3: ";
}
//co<<"push:"<<z.mn<<" "<<z.ma<<" "<<z.mb<<"--"<<z.la<<" "<<z.lb<<"|"<<z.va.x<<"="<<z.va.y<<" "<<z.vb.x<<"="<<z.vb.y<<" "<<z.vc.x<<"="<<z.vc.y<<"\n";
return z;
};
struct Segment_Tree{
Tree t[N<<2];
#define ls x<<1
#define rs x<<1|1
inline void add(int x,int k)
{
t[x].tag+=k,t[x].mn+=k;
}
void pushup(int x)
{
t[x]=t[ls]+t[rs];
}
void pushdown(int x)
{
if(!t[x].tag)return;
add(ls,t[x].tag);add(rs,t[x].tag);
t[x].tag=0;
}
void build(int x,int l,int r)
{
if(l==r)
{
t[x]=_new(l);
return;
}
int mid=l+r>>1;
build(ls,l,mid);build(rs,mid+1,r);
pushup(x);
}
void push(int x,int l,int r,int pos)
{
if(l==r)return ;
pushdown(x);
int mid=l+r>>1;
if(pos<=mid)push(ls,l,mid,pos);
if(mid<pos)push(rs,mid+1,r,pos);
pushup(x);
}
void upd(int x,int l,int r,int L,int R,int k)
{
if(R<l||r<L)return ;
if(L<=l&&r<=R)
{
add(x,k);
return ;
}
int mid=l+r>>1;
if(L<=mid)upd(ls,l,mid,L,R,k);
if(mid<R)upd(rs,mid+1,r,L,R,k);
pushup(x);
}
#undef ls
#undef rs
}T;
#define rt T.t[1]
void work()
{
cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
a[0]=b[0]=inf;
T.build(1,0,n);
for(int i=1;i<=m;i++)
{
int k;
Node u;
if(rt.va<rt.vb){u=rt.va,k=1;}
else {u=rt.vb,k=-1;}
ans+= a[u.x]+b[u.y];
a[u.x]=b[u.y]=inf;
//co<<u.x<<" "<<u.y<<"\n";
T.push(1,0,n,u.x);T.push(1,0,n,u.y);
T.upd(1,0,n,min(u.x,u.y),max(u.x,u.y)-1,k);
}
printf("%lld",ans);
}
#undef rt
#undef int
int main()
{
//freopen("P4694.in","r",stdin);freopen("P4694.out","w",stdout);
work();
return 0;
}
posted @   liuboom  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示