Luogu P5470 [NOI2019]序列
题目
可以直接贪心,但是用模拟费用流推的话会更轻松。
首先有一个显然的建图方式:
\(S\)到\(0\)流量为\(k\),费用为\(0\)。
\(0\)到\(a_i\)流量为\(1\),费用为\(-a_i\)。
\(a_i\)到\(b_i\)流量为\(1\),费用为\(0\)。
\(b_i\)到\(T\)流量为\(1\),费用为\(-b_i\)。
\(a_i\)到\(c\)流量为\(1\),费用为\(0\)。
\(c\)到\(d\)流量为\(k-l\),费用为\(0\)。
\(d\)到\(b_i\)流量为\(1\),费用为\(0\)。
然后我们来模拟一下费用流。
首先我们肯定先把\(c->d\)这里流满,因为这个是没有\(a_i->b_i\)的配对限制的,一定会更优。
我们先在左右各选\(k-l\)(可能有重复的),能够直接匹配流\(a_i->b_i\),否则走\(c->d\)。
接下来我们先把\(c->d\)流满。
也就是直接在\(a,b\)中各选一个最大的。
然后我们接下来每次保证增加一对\(a_i->b_i\),这样就能够保证正确性了。
这里有三种情况:
第一种:我们选一个\(a_i,b_i\)都未选的\(a_i+b_i\)最大的。
第二种:对于一个已被选择的\(a_i\)(对应的\(b_i\)尚未被选),我们给它\(b_i\)配对,并且在未被选的\(a_j\)中挑一个最大的。
反应在流量网络上就是这样:
原本是\(a_i->c->d->b_x\)(这个\(b_x\)实际上可以是任意一个流量从\(d\)来的点)。
我们把它改成\(a_i->b_i\),\(a_j->c->d->b_x\)。
第三种:把第二种的\(a,b\)反过来。
这样我们每次选取能够使答案增加最大的一种方法走,就能够解决这个问题了。
具体而言,我们需要开几个堆记录未被选的\(a_i,b_i,a_i+b_i\)的最大值的下标\(i\),已选的\(a_i(b_i)\)对应的\(b_i(a_i)\)中最大值的下标\(i\)。
代码比较长,凑合着看吧。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],*iS,*iT;
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
int read(){int x=0;char c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+(c^48),c=Get();return x;}
}
using namespace IO;
int max(int a,int b){return a>b? a:b;}
const int N=1000007;
LL ans;int T,n,l,k,id[N],a[N],b[N],fa[N],fb[N];
struct nodea{int id;};struct nodeb{int id;};struct nodec{int id;};
int operator<(nodea i,nodea j){return a[i.id]<a[j.id];}
int operator<(nodeb i,nodeb j){return b[i.id]<b[j.id];}
int operator<(nodec i,nodec j){return a[i.id]+b[i.id]<a[j.id]+b[j.id];}
int cmpa(int i,int j){return a[i]>a[j];}
int cmpb(int i,int j){return b[i]>b[j];}
priority_queue<nodea>ra,sa;priority_queue<nodeb>rb,sb;priority_queue<nodec>rc;
int main()
{
int i,j,num,va,vb,vc,mx;
for(T=read();T;--T)
{
n=read(),k=read(),l=read(),memset(fa,0,sizeof fa),memset(fb,0,sizeof fb),ans=num=va=vb=vc=0;
while(!ra.empty())ra.pop();while(!rb.empty())rb.pop();while(!sa.empty())sa.pop();while(!sb.empty())sb.pop();while(!rc.empty())rc.pop();
for(i=1;i<=n;++i) a[i]=read(),id[i]=i;
for(i=1;i<=n;++i) b[i]=read();
sort(id+1,id+n+1,cmpa); for(i=1;i<=k-l;++i) ans+=a[id[i]],fa[id[i]]=1;
sort(id+1,id+n+1,cmpb); for(i=1;i<=k-l;++i) ans+=b[id[i]],fb[id[i]]=1;
for(i=1;i<=n;++i)
if(fa[i]&&fb[i]) ++num;
else if(fa[i]) rb.push((nodeb){i}),sb.push((nodeb){i});
else if(fb[i]) ra.push((nodea){i}),sa.push((nodea){i});
else ra.push((nodea){i}),rb.push((nodeb){i}),rc.push((nodec){i});
while(l--)
{
while(!ra.empty()&&fa[ra.top().id])ra.pop();
while(!rb.empty()&&fb[rb.top().id])rb.pop();
while(!rc.empty()&&(fa[rc.top().id]||fb[rc.top().id]))rc.pop();
while(!sa.empty()&&fa[sa.top().id])sa.pop();
while(!sb.empty()&&fb[sb.top().id])sb.pop();
if(num)
{
i=ra.top().id,j=rb.top().id,ans+=a[i]+b[j],fa[i]=fb[j]=1,--num;
if(!fb[i]) sb.push((nodeb){i});
if(!fa[j]) sa.push((nodea){j});
if(i==j)++num;else{if(fa[i]&&fb[i])++num;if(fa[j]&&fb[j])++num;}
continue;
}
if(!sb.empty()) i=ra.top().id,j=sb.top().id,va=a[i]+b[j];
if(!sa.empty()) i=rb.top().id,j=sa.top().id,vb=a[j]+b[i];
if(!rc.empty()) i=rc.top().id,vc=a[i]+b[i];
mx=max(vc,max(va,vb)),ans+=mx;
if(va==mx) i=ra.top().id,j=sb.top().id,fa[i]=fb[j]=1,fb[i]? ++num:(sb.push((nodeb){i}),0);
else if(vb==mx) i=rb.top().id,j=sa.top().id,fa[j]=fb[i]=1,fa[i]? ++num:(sa.push((nodea){i}),0);
else if(vc==mx) i=rc.top().id,rc.pop(),fa[i]=fb[i]=1;
}
printf("%lld\n",ans);
}
}
当然如果我们一开始啥都不干(把“我们先在左右各选\(k-l\)(可能有重复的),能够直接匹配流\(a_i->b_i\),否则走\(c->d\)”这一步去掉)也是可行的,改几个地方就行了。
代码会短一些,不过常数会大一些。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],*iS,*iT;
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
int read(){int x=0;char c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+(c^48),c=Get();return x;}
}
using namespace IO;
int max(int a,int b){return a>b? a:b;}
const int N=1000007;
LL ans;int T,n,l,k,a[N],b[N],fa[N],fb[N];
struct nodea{int id;};struct nodeb{int id;};struct nodec{int id;};
int operator<(nodea i,nodea j){return a[i.id]<a[j.id];}
int operator<(nodeb i,nodeb j){return b[i.id]<b[j.id];}
int operator<(nodec i,nodec j){return a[i.id]+b[i.id]<a[j.id]+b[j.id];}
int cmpa(int i,int j){return a[i]>a[j];}
int cmpb(int i,int j){return b[i]>b[j];}
priority_queue<nodea>ra,sa;priority_queue<nodeb>rb,sb;priority_queue<nodec>rc;
int main()
{
int i,j,num,va,vb,vc,mx;
for(T=read();T;--T)
{
n=read(),k=read(),l=read(),memset(fa,0,sizeof fa),memset(fb,0,sizeof fb),ans=va=vb=vc=0,num=k-l;
while(!ra.empty())ra.pop();while(!rb.empty())rb.pop();while(!sa.empty())sa.pop();while(!sb.empty())sb.pop();while(!rc.empty())rc.pop();
for(i=1;i<=n;++i) a[i]=read();
for(i=1;i<=n;++i) b[i]=read();
for(i=1;i<=n;++i) ra.push((nodea){i}),rb.push((nodeb){i}),rc.push((nodec){i});
while(k--)
{
while(!ra.empty()&&fa[ra.top().id])ra.pop();
while(!rb.empty()&&fb[rb.top().id])rb.pop();
while(!rc.empty()&&(fa[rc.top().id]||fb[rc.top().id]))rc.pop();
while(!sa.empty()&&fa[sa.top().id])sa.pop();
while(!sb.empty()&&fb[sb.top().id])sb.pop();
if(num)
{
i=ra.top().id,j=rb.top().id,ans+=a[i]+b[j],fa[i]=fb[j]=1,--num;
if(!fb[i]) sb.push((nodeb){i});
if(!fa[j]) sa.push((nodea){j});
if(i==j)++num;else{if(fa[i]&&fb[i])++num;if(fa[j]&&fb[j])++num;}
continue;
}
if(!sb.empty()) i=ra.top().id,j=sb.top().id,va=a[i]+b[j];
if(!sa.empty()) i=rb.top().id,j=sa.top().id,vb=a[j]+b[i];
if(!rc.empty()) i=rc.top().id,vc=a[i]+b[i];
mx=max(vc,max(va,vb)),ans+=mx;
if(va==mx) i=ra.top().id,j=sb.top().id,fa[i]=fb[j]=1,fb[i]? ++num:(sb.push((nodeb){i}),0);
else if(vb==mx) i=rb.top().id,j=sa.top().id,fa[j]=fb[i]=1,fa[i]? ++num:(sa.push((nodea){i}),0);
else if(vc==mx) i=rc.top().id,rc.pop(),fa[i]=fb[i]=1;
}
printf("%lld\n",ans);
}
}
Luogu P5470 [NOI2019]序列的更多相关文章
- 【题解】Luogu P5470 [NOI2019]序列
原题传送门 同步赛上我一开始想了个看似正确却漏洞百出的贪心:按\(a_i+b_i\)的和从大向小贪心 随便想想发现是假的,然后就写了个28pts的暴力dp 杜神后半程说这题就是个贪心,但我没时间写了 ...
- luogu P5470 [NOI2019]序列 dp 贪心 费用流 模拟费用流
LINK:序列 考虑前20分 容易想到爆搜. 考虑dp 容易设\(f_{i,j,k,l}\)表示前i个位置 选了j对 且此时A选择了k个 B选择了l个的最大值.期望得分28. code //#incl ...
- 洛谷 P5470 - [NOI2019] 序列(反悔贪心)
洛谷题面传送门 好几天没写题解了,写篇题解意思一下(大雾 考虑反悔贪心,首先我们考虑取出 \(a,b\) 序列中最大的 \(k\) 个数,但这样并不一定满足交集 \(\ge L\) 的限制,因此我们需 ...
- [luogu P3648] [APIO2014]序列分割
[luogu P3648] [APIO2014]序列分割 题目描述 小H最近迷上了一个分隔序列的游戏.在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列.为了得到k+1个子序 ...
- Luogu P5469 [NOI2019]机器人 (DP、多项式)
不用FFT的多项式(大雾) 题目链接: https://www.luogu.org/problemnew/show/P5469 (这题在洛谷都成绿题了海星) 题解: 首先我们考虑,一个序列位置最右边的 ...
- [Luogu 2642] 双子序列最大和
Description 给定一个长度为n的整数序列,要求从中选出两个连续子序列,使得这两个连续子序列的序列和之和最大,最终只需输出最大和.一个连续子序列的和为该子序列中所有数之和.每个连续子序列的最小 ...
- 【题解】Luogu P2572 [SCOI2010]序列操作
原题传送门:P2572 [SCOI2010]序列操作 这题好弱智啊 裸的珂朵莉树 前置芝士:珂朵莉树 窝博客里对珂朵莉树的介绍 没什么好说的自己看看吧 操作1:把区间内所有数推平成0,珂朵莉树基本操作 ...
- Luogu 3321 [SDOI2015]序列统计
BZOJ 3992 点开这道题之后才发现我对原根的理解大概只停留在$998244353$的原根是$3$…… 关于原根: 点我 首先写出$dp$方程,设$f_{i, j}$表示序列长度为$i$当前所有数 ...
- 匈牙利算法 - Luogu 1963 变换序列
P1963 变换序列 题目描述 对于N个整数0,1,-,N-1,一个变换序列T可以将i变成Ti,其中:Ti∈{0,1,-,N-1}且 {Ti}={0,1,-,N-1}. x,y∈{0,1,-,N-1} ...
随机推荐
- 【Islands and Bridges】题解
题目 题目描述 给定一些岛屿和一些连接岛屿的桥梁,大家都知道汉密尔顿路是访问每个岛屿一次的路线,在我们这个地图中,每个岛屿有个正整数的权值,表示这个岛屿的观赏价值.假设一共有N个岛屿,用Vi表示岛屿C ...
- 参数上使用自定义注解在aop中无法获取到该参数
https://ask.csdn.net/questions/769477 /** * 环绕增强,验证权限 * @param joinPoint 目标对象 * @param authCheck 自定义 ...
- Mac 找文件或文件夹,以及开启其他程序,截图快捷键
Mac 图形化界面对操作惯 Win 的人来说比较奇怪. 有一组超级有用的快捷键,control + 空格 按下后会出现一个搜索框,输入计算机上任何你想要找的资源即可打开. 截取全屏:快捷键(Shift ...
- 用JavaServiceWrapper将JAVA程序发布成Windows服务
怎么把jar文件做成系统服务,比较多的解决方案是使用 wrapper-windows 这个软件包.这个软件包的强大之处是能把jre环境也给打进去,这个服务可以正常运行在根本没有jre环境即就没有安装J ...
- B. Uniqueness
B. Uniqueness 给定一个序列,要求删除一段连续子段,满足删掉子段后每个元素唯一 求最小子段长度 枚举起点,二分子段长度 记得先sort 再unique #include<bits/s ...
- Spotlight_on_mysql 安装和监控
一.下载 1.下载并安装 mysql-connector-3.51.30 2.下载并安装 Quest_Spotlight-on-MySQL_80.exe 二.填写注册码 Authorization K ...
- Linux读写执行权限
Linux 将访问文件的用户分为 3 类,分别是文件的所有者,所属组(也就是文件所属的群组)以及其他人. 最常见的文件权限有 3 种,即对文件的读(用 r 表示). 写(用 w 表示). 执行(用 x ...
- 第四周实验总结&实验报告
实验二 Java简单类与对象 实验目的 掌握类的定义,熟悉属性.构造函数.方法的作用,掌握用类作为类型声明变量和方法返回值: 理解类和对象的区别,掌握构造函数的使用,熟悉通过对象名引用实例的方法和属性 ...
- AndroidStudio设置SVN忽略文件
方法一: 在SVN中进行设置: 在空白处右键单击,选择TortoiseSVN -> Settings ->General:在General界面找到Global ignore pattern ...
- angular 的配置文件的应用
为什么要使用 angular 的配置文件: 好处:我们可以在一个页面上,实现多个页面的跳转的感觉,但只是在一个页面上进行的操作: 我们的准备工作:下载 angular-route.js 插件 在依赖模 ...