中国(北方)大学生程序设计训练赛(第一周)(Problem B: 埃蒙的时空航道-最小割转dp+贪心)

Description

埃蒙的军队部署在 n 个星球,星球的编号分别为 1,⋯ ,n1,\cdots,n1,,n
埃蒙在第 iii 个星球驻扎着 pip_ ipi个单位的军队。据可靠情报,若干天后指挥官们会往第 iii 个星球派出 si 个单位的军队。

星球两两之间具有时空航道,埃蒙可以从起点星球发射一架最多容纳 ccc 个单位部队的穿梭机到终点星球。但是,这些时空航道都是单向的,穿梭机只能从编号更小的星球运送部队到编号更大的星球。埃蒙可以以任意顺序发射这些穿梭机,但每条时空航道只能使用一次。

111个单位的埃蒙部队可以牵制111个单位的指挥官部队。现在埃蒙想要知道他最多能牵制多少指挥官们的军队。

n≤105n \le 10^5n105

考虑建立网络流,从SSS向所有星球连边,容量sis_isi,从星球向TTT连边容量cic_ici,编号小的星球向大的星球连边,容量ccc,答案即位最大流

考虑dp求最小割,有
dpi,j=min(dpi−1,j−1+pi,dpi−1,j+si+c∗j)dp_{i,j}=min(dp_{i-1,j-1}+p_i,dp_{i-1,j}+s_i+c*j)dpi,j=min(dpi1,j1+pi,dpi1,j+si+cj)
我们发现
割集每多包含一个节点i,容量增加pi−si+c∗(i−k)p_i-s_i+c*(i-k)pisi+c(ik),k为割集中已有点数,我们可以将k分离变量,令wi=pi−si+c∗iw_i=p_i-s_i+c*iwi=pisi+ci,每次把最小的wiw_iwi加入点集,和答案取最小值

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <functional>
#include <cstdlib>
#include <queue>
#include <stack>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=Pre[x];p;p=Next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=Next[p])  
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,127,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define INF (2139062143)
#define F (100000007)
#define pb push_back
#define mp make_pair 
#define fi first
#define se second
#define vi vector<int> 
#define pi pair<int,int>
#define SI(a) ((a).size())
typedef long long ll;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return (a-b+llabs(a-b)/F*F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
int read()
{
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
} 
#define MAXN (123456)
ll n,c;
ll a[MAXN];
priority_queue<ll> q;
int main() {
    // freopen("B.in","r",stdin);
    scanf("%lld%lld",&n,&c);
    ll tot=0;
    For(i,n) scanf("%lld",&a[i]),tot+=a[i];
    For(i,n) {
        q.push(a[i]-read()-(ll)c*(n-i));
    }
	ll ans=tot;
    For(i,n) {
        tot-=q.top()+c*(i-1);
        ans=min(ans,tot);
        q.pop();
    }
    cout<<ans<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值