P5470-[NOI2019]序列【模拟费用流】
正题
题目链接:https://www.luogu.com.cn/problem/P5470
题目大意
两个长度为\(n\)的序列\(a,b\),求出它们两个长度为\(K\)的子序列,且这两个子序列至少有\(L\)个位置下标相等。
求最大化两个子序列的和。
\(T\leq 10,1\leq n\leq 2\times 10^5, \sum n\leq 10^6\)
解题思路
先考虑一个简单的费用流做法,我们对于\(a_i\)建在左边,\(b_i\)建在右边。
然后开设一条流量为\(K-L\)的边供所有\(a_i\)连接所有\(b_i\)表示这些可以随便匹配,然后剩下的都是\(a_i\)连接\(b_i\)就好了。
但是费用流显然会\(T\),所以考虑一下怎么模拟这个过程。
记流\(K-L\)的为自由流,其他的为限制流,我们肯定会把自由流先流满,模拟的过程就是每次选最大的两个匹配,当然如果这两个恰好是一起的就流限制流就好了。
自由流流满之后有三种操作
- 正常流限制流,选取最大的一个两边都未匹配的流过去
- 寻找增广路,我们肯定是会退流一条自由流匹配的,此时我们需要找到一个限制流。这里拿\(a\)举例,也就是我们需要找到匹配过的\(a_i\)且\(b_i\)未匹配,此时我们增广\(a_i->b_i\),那么之前与\(a_i\)匹配的就需要找一个最大的\(b_i\)匹配。\(b\)同理。
但是这样会发现还是有问题,比如有时我们会出现\(a_x<->b_y,b_x<->a_y\)的情况,此时相当与从\(a_{x/y}<->b_{x/y}\)增广。
每次匹配完之后判断一下这种情况就好了。开5个堆维护即可
时间复杂度\(O(n\log n)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define mp(x,y) make_pair(x,y)
#define val first
#define id second
using namespace std;
const int N=2e5+10;
int T,n,K,L,Free,a[N],b[N],link[N],rink[N];
priority_queue<pair<int,int> >H,Ano,Bno,Ause,Buse;
long long ans;
void Change(int x){
if(link[x]&&rink[x]&&link[x]!=x){
if(link[x]==rink[x])Free++;
rink[link[x]]=rink[x];
link[rink[x]]=link[x];
link[x]=rink[x]=x;Free++;
}
return;
}
int main()
{
scanf("%d",&T);
while(T--){
memset(link,0,sizeof(link));
memset(rink,0,sizeof(rink));
while(!H.empty())H.pop();
while(!Ano.empty())Ano.pop();
while(!Bno.empty())Bno.pop();
while(!Ause.empty())Ause.pop();
while(!Buse.empty())Buse.pop();
ans=0;
scanf("%d%d%d",&n,&K,&L);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
for(int i=1;i<=n;i++){
H.push(mp(a[i]+b[i],i));
Ano.push(mp(a[i],i));
Bno.push(mp(b[i],i));
}
Free=K-L;
for(int i=1;i<=K;i++){
while(!Ano.empty()&&link[Ano.top().id])Ano.pop();
while(!Bno.empty()&&rink[Bno.top().id])Bno.pop();
if(Free){
int x=Ano.top().id;
int y=Bno.top().id;
link[x]=y;rink[y]=x;
Ause.push(mp(b[x],x));
Buse.push(mp(a[y],y));
if(x!=y)Free--;
Change(x);Change(y);
ans+=a[x]+b[y];
continue;
}
//1. anone<->bnone
//2. anone<->buse ause<->bnone
//3. bnone<->ause buse<->anone
int ans1=-1e9,ans2=-1e9,ans3=-1e9;
while(!H.empty()&&(link[H.top().id]||rink[H.top().id]))H.pop();
if(!H.empty())ans1=H.top().val;
while(!Buse.empty()&&(link[Buse.top().id]||rink[Buse.top().id]==Buse.top().id))Buse.pop();
if(!Buse.empty()&&!Bno.empty())ans2=Buse.top().val+Bno.top().val;
while(!Ause.empty()&&(link[Ause.top().id]==Ause.top().id||rink[Ause.top().id]))Ause.pop();
if(!Ause.empty()&&!Ano.empty())ans3=Ause.top().val+Ano.top().val;
if(ans1>=ans2&&ans1>=ans3){
int x=H.top().id;
link[x]=rink[x]=x;
}
else if(ans2>=ans1&&ans2>=ans3){
int x=Buse.top().id,y=Bno.top().id,z=rink[x];
link[z]=y;rink[y]=z;Free+=(z==y);
link[x]=rink[x]=x;
Buse.push(mp(a[y],y));
Change(y);Change(z);
}
else{
int x=Ause.top().id,y=Ano.top().id,z=link[x];
link[y]=z;rink[z]=y;Free+=(z==y);
link[x]=rink[x]=x;
Ause.push(mp(b[y],y));
Change(y);Change(z);
}
ans+=max(max(ans1,ans2),ans3);
}
printf("%lld\n",ans);
}
return 0;
}
P5470-[NOI2019]序列【模拟费用流】的更多相关文章
- luogu P5470 [NOI2019]序列 dp 贪心 费用流 模拟费用流
LINK:序列 考虑前20分 容易想到爆搜. 考虑dp 容易设\(f_{i,j,k,l}\)表示前i个位置 选了j对 且此时A选择了k个 B选择了l个的最大值.期望得分28. code //#incl ...
- [NOI2019]序列(模拟费用流)
题意: 有两个长度为n的序列,要求从每个序列中选k个,并且满足至少有l个位置都被选,问总和最大是多少. \(1\leq l\leq k\leq n\leq 2*10^5\). 首先,记录当前考虑到的位 ...
- 模拟费用流 & 可撤销贪心
1. CF730I Olympiad in Programming and Sports 大意: $n$个人, 第$i$个人编程能力$a_i$, 运动能力$b_i$, 要选出$p$个组成编程队, $s ...
- Codeforces 280D k-Maximum Subsequence Sum [模拟费用流,线段树]
洛谷 Codeforces bzoj1,bzoj2 这可真是一道n倍经验题呢-- 思路 我首先想到了DP,然后矩阵,然后线段树,然后T飞-- 搜了题解之后发现是模拟费用流. 直接维护选k个子段时的最优 ...
- 【BZOJ3502/2288】PA2012 Tanie linie/【POJ Challenge】生日礼物 堆+链表(模拟费用流)
[BZOJ3502]PA2012 Tanie linie Description n个数字,求不相交的总和最大的最多k个连续子序列. 1<= k<= N<= 1000000. Sam ...
- 贪心(模拟费用流):NOIP2011 观光公交
[问题描述] 风景迷人的小城Y 市,拥有n 个美丽的景点.由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供更便捷的交通服务.观光公交车在第0 分钟出现在1号景点,随后依次前往2. ...
- BZOJ4977[Lydsy1708月赛]跳伞求生——贪心+堆+模拟费用流
题目链接: 跳伞求生 可以将题目转化成数轴上有$n$个人和$m$个房子,坐标分别为$a_{i}$和$b_{i}$,每个人可以进一个他左边的房子,每个房子只能进一个人.每个房子有一个收益$c_{i}$, ...
- 【bzoj1150】[CTSC2007]数据备份Backup 模拟费用流+链表+堆
题目描述 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏 ...
- [UOJ455][UER #8]雪灾与外卖——堆+模拟费用流
题目链接: [UOJ455]雪灾与外卖 题目描述:有$n$个送餐员(坐标为$x_{i}$)及$m$个餐厅(坐标为$y_{i}$,权值为$w_{i}$),每个送餐员需要前往一个餐厅,每个餐厅只能容纳$c ...
- BZOJ4849[Neerc2016]Mole Tunnels——模拟费用流+树形DP
题目描述 鼹鼠们在底下开凿了n个洞,由n-1条隧道连接,对于任意的i>1,第i个洞都会和第i/2(取下整)个洞间有一条隧 道,第i个洞内还有ci个食物能供最多ci只鼹鼠吃.一共有m只鼹鼠,第i只 ...
随机推荐
- VMware 启动虚拟机黑屏(Ubuntu)
之前启动都很正常,不知道为啥,最近启动,老出现这种黑屏得情况: 解决方法: 在搜索栏里搜索cmd 巴拉了一下谷歌,发现了问题: 虚拟机和主机之间的通信,基本上是以 socket 的方式进行通信的(这里 ...
- PHP随手记2--获取随机n位不重复字符
定义一个函数返回26英文字母中n位不重复随机字符 基本思路是利用内置函数生成随机数,取出该位置字母之后将其删除,再进行下一次随机,最后实现字符串拼接就ok! 代码很简单,通俗易懂,直接上代码吧: 1 ...
- new和delete关键字
new关键字创建出来的对象位于什么地方?很明显嘛,new关键字创建出来的对象一定位于堆空间,这种说法一定正确吗?本篇博客帮你揭开其神秘的面纱. 被忽略的事实new/delete的本质是C++预定义的操 ...
- ubuntu黑屏无法进入系统【Recovery Mode急救】
一.问题 前言:因为一次美化配置ubuntu导致系统启动黑屏,无法进入系统.之前并没有系统备份,后果严重还好修复了,记录下修复步骤备用. 事件:就是因为修改了 /usr/share/gnome-sh ...
- Git脑图
ps:有时我们想一台有不同的git账号对应不同的git仓库时(gitLab/gitHub)时,除了全局的用户配置定义,我们可以为不同仓库自定义不同用户名和邮件 1.查询全局的配置:git config ...
- SpringMVC笔记(3)
一.SpringMVC 拦截器 1.1 快速入门 步骤 创建拦截器类实现HandlerInterceptor接口 public class MyInterceptor01 implements Han ...
- 洛谷P3104 Counting Friends G 题解
题目 [USACO14MAR]Counting Friends G 题解 这道题我们可以将 \((n+1)\) 个边依次去掉,然后分别判断去掉后是否能满足.注意到一点, \(n\) 个奶牛的朋友之和必 ...
- 1.深入TiDB:初见TiDB
转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/584 本篇文章应该是我研究的 TiDB 的第一篇文章,主要是介绍整个 ...
- Python中正则表达式简介
目录 一.什么是正则表达式 二.正则表达式的基础知识 1. 原子 1)普通字符作为原子 2)非打印字符作为原子 3) 通用字符作为原子 4) 原子表 2. 元字符 1)任意匹配元字符 2)边界限制元字 ...
- 放码来战!HMS Core线上Codelabs挑战赛正式开始
亲爱的开发者,在1024程序员节即将到来之际,HMS Core准备了一场线上Codelabs挑战赛,现向你发出诚挚邀请,希望你能将新奇的想法和对产品的思考融入代码,用技术与世界对话. HMS Core ...