【Atcoder思维训练】AT2672 [AGC018C] Coins
Preface
一道标准的模拟网络流(费用流)题目,蒟蒻第一次做反悔贪心。
Problem
有
Solution
其实看到这种有后效性的决策题目的时候我们首先想到的应该是网络流,比如像这道题,我们就可以立刻建出一个费用流模型,但是这道题的数据范围很鬼畜,很明显朴素的费用流是跑不过去的。
这个时候我们就不得不仔细思考一下费用流的本质了,对于一个分配完毕的网络,费用流可以通过若干次推流操作来增大收益,这个推流的过程实际上就是对物品进行重分配。
那我们就有一个问题了,既然费用流要在图上跑很久才能出结果,我们能不能直接模拟物品重分配来减小复杂度呢?根据题意,我们的重分配方案一共五种:
a->b b->c c->a
a->c c->b b->a
a->c c->a
a->b b->a
b->c c->b
其中三个字母分别代表不同币种。
其中 -> 表示一个原本用在一个币种的人转换到另外一个币种,很显然这些人对空间是本质相同的,所以我们可以分别选择最优的人进行求解。
举个例子,上文中第一行重分配方式,我们就可以先选择从金币到银币贡献最大者,然后选择银币到铜币贡献最大者,最后选择铜币到金币贡献最大者。
然后我们发现这个过程其实可以用六个堆进行模拟,直到无流可退我们就结束计算。
最后我们还有一个疑问:上述的过程叫反悔贪心,但是普通费用流复杂度为
这就不得不说反悔的具体次数了,很显然我们将反悔一次的复杂度降为了
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int n,x,y,z,sum;
int a[N],b[N],c[N];int st[N];
struct node{
int id,data;
bool operator <(const node &a)const{
return data<a.data;
}
};
node nmax(node a,node b){
if(a.data<b.data)return b;
else return a;
}
priority_queue <node> Q[7];
//a->b b->c c->a
//a->c c->b b->a
//a->c c->a
//a->b b->a
//b->c c->b
int mo[N][7];
void update(int x){
Q[st[x]*2-1].push((node){x,mo[x][st[x]*2-1]});
Q[st[x]*2].push((node){x,mo[x][st[x]*2]});
}
void modify(int x){
if(x==1||x==4)st[Q[1].top().id]=2;
if(x==2||x==3)st[Q[2].top().id]=3;
if(x==1||x==5)st[Q[4].top().id]=3;
if(x==2||x==4)st[Q[3].top().id]=1;
if(x==2||x==5)st[Q[6].top().id]=2;
if(x==1||x==3)st[Q[5].top().id]=1;
update(Q[1].top().id);update(Q[2].top().id);
update(Q[3].top().id);update(Q[4].top().id);
update(Q[5].top().id);update(Q[6].top().id);
}
signed main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin>>x>>y>>z;n=x+y+z;
for(int i=1;i<=x;i++){
cin>>a[i]>>b[i]>>c[i];st[i]=1;sum+=a[i];
Q[1].push((node){i,b[i]-a[i]});Q[2].push((node){i,c[i]-a[i]});
}
for(int i=x+1;i<=x+y;i++){
cin>>a[i]>>b[i]>>c[i];st[i]=2;sum+=b[i];
Q[3].push((node){i,a[i]-b[i]});Q[4].push((node){i,c[i]-b[i]});
}
for(int i=x+y+1;i<=x+y+z;i++){
cin>>a[i]>>b[i]>>c[i];st[i]=3;sum+=c[i];
Q[5].push((node){i,a[i]-c[i]});Q[6].push((node){i,b[i]-c[i]});
}
for(int i=1;i<=n;i++){
mo[i][1]=b[i]-a[i];mo[i][2]=c[i]-a[i];
mo[i][3]=a[i]-b[i];mo[i][4]=c[i]-b[i];
mo[i][5]=a[i]-c[i];mo[i][6]=b[i]-c[i];
}
while(true){
//a->b b->c c->a
//a->c c->b b->a
//a->c c->a
//a->b b->a
//b->c c->b
int ab=-1e18;int ac=-1e18;int ba=-1e18;
int bc=-1e18;int ca=-1e18;int cb=-1e18;
for(int i=1;i<=6;i++){
while(!Q[i].empty()&&st[Q[i].top().id]!=((i+1)/2))
Q[i].pop();
}
if(!Q[1].empty())ab=Q[1].top().data;
if(!Q[2].empty())ac=Q[2].top().data;
if(!Q[3].empty())ba=Q[3].top().data;
if(!Q[4].empty())bc=Q[4].top().data;
if(!Q[5].empty())ca=Q[5].top().data;
if(!Q[6].empty())cb=Q[6].top().data;
node res=nmax(nmax(nmax(nmax((node){4,ab+ba},(node){5,bc+cb})
,(node){3,ac+ca}),(node){2,ac+cb+ba}),(node){1,ab+bc+ca});
if(res.data<=0)break;sum+=res.data;modify(res.id);
}
cout<<sum;
}