模擬費用流學習筆記

模擬費用流學習筆記

就是用各種東西維護費用流,有退流的可以理解爲反悔貪心,否則直接貪心。

來點典題

uoj445 UER#8 B

先讓每個送餐員匹配左邊的最優的餐廳,這個過程可以用堆維護餐廳的 \(w-y\) 的最小值。

匹配右邊的情況考慮反悔,讓右邊的餐廳匹配送餐員,再開個堆維護 \(-v-x\)\(v\)爲這個送餐員的最優匹配。然後找到餐廳之後就在這個堆裏找一個匹配,這個也要反悔,在餐廳的堆里加入 \(-v+(w-y)\)

還有用後面的餐廳匹配前面已經匹配的送餐員的情況,在送餐員的堆中加入\(-w-y\)

還有用後面的送餐員匹配前面已經匹配的餐廳的情況,這種一定不會更優。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define FOR(i,a,b) for(ll i=a;i<=b;++i)
ll read(){
	ll x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x;
} 
const ll  N = 2e5+200;
ll ans;
ll n,m;
ll x[N],y[N],w[N],c[N];
struct node{
	ll w;ll cnt;
	node(ll w=0,ll cnt=0):w(w),cnt(cnt){}
};
struct cmp{
	ll operator()(node a,node b){
		return a.w>b.w;
	}
};
priority_queue<node,vector<node>,cmp> q1,q2;
ll inf = 2e12;
void work1(ll now){
	ll res=inf;
	if(!q1.empty()){
		node fr=q1.top();q1.pop();
		res=fr.w+x[now];
		if((--fr.cnt)>0) q1.push(fr);
	}
	ans+=res;
	q2.push(node(-res-x[now],1));
}
void work2(ll now){
	ll rc=c[now];
	while(!q2.empty()&&rc>0){
		ll nw=q2.top().w+w[now]+y[now];
		if(nw>=0) break;
		if(rc<=q2.top().cnt){
			ans+=nw*rc;
			q1.push(node(-nw+w[now]-y[now],rc));
			node fr=q2.top();fr.cnt-=rc;rc=0;q2.pop();if(fr.cnt!=0) q2.push(fr);
			break;
		}else{
			rc-=q2.top().cnt;
			ans+=nw*q2.top().cnt;
			q1.push(node(-nw+w[now]-y[now],q2.top().cnt));
			q2.pop();
		}
	}
	if(c[now]>rc){
		q2.push(node(-w[now]-y[now],c[now]-rc));
	}
	if(rc){
		q1.push(node(w[now]-y[now],rc));
	}
}
int main(){
	n=read(),m=read();
	FOR(i,1,n) x[i]=read();
	ll tot=0;
	FOR(i,1,m){
		y[i]=read(),w[i]=read(),c[i]=read();tot+=c[i];
	}
	if(tot<n){
		printf("-1");
		return 0;
	}
	for(ll i=1,j=1;i<=n||j<=m;){
		if(i<=n&&(j>m||x[i]<y[j])){
			work1(i++);
		}else work2(j++);
	}
	printf("%lld\n",ans);
	return 0;
}

loj6405. 「ICPC World Finals 2018」征服世界

在LCA合併,添加反悔。可以發現不會同時反悔,所以寫起來很方便。

注意一個都不匹配是最優的,而題目要求是所有軍隊都匹配,所以先把每個匹配的權值減去 \(\inf\) 就能讓匹配數優先級大於權值。最後還要加上 \(\sum y_i*\inf\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define FOR(i,a,b) for(ll i=a;i<=b;++i)
ll read(){
	ll x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x;
} 
const ll  N = 2.5e5+200;
ll n,m;
ll x[N],y[N],w[N],c[N];
struct node{
	mutable ll w;ll cnt;
	node(ll w=0,ll cnt=0):w(w),cnt(cnt){}
	int operator <(node b){
		return w<b.w;
	}
}val[N*30];
int son[N*30],nex[N*30],num=0;
struct heap{
	int merge(int u,int v){
		if(!u||!v) return u+v;
		if(val[v]<val[u]) swap(u,v);nex[v]=son[u];son[u]=v;return u;
	}
	int merges(int u){
		if(!u) return 0;if(!nex[u]) return u;
		int v1=nex[u],v2=nex[v1];
		nex[u]=nex[v1]=0;return merge(merge(u,v1),merges(v2));
	}
	int rt;
	void pop(){rt=merges(son[rt]);}
	void join(int x){rt=merge(rt,x);}
	node &top(){return val[rt];}
	void push(node now){val[++num]=now;rt=merge(rt,num);}
	int empty(){return rt==0;}
}H[N*2],M[N*2];
#define pii pair<int,int>
#define fi first
#define se second
vector<pii>G[N];
const ll inf = 1e12;
ll ans=0;
void dfs(int now,int pre,ll dep){
	H[now].push(node(dep,x[now]));
	M[now].push(node(dep-inf,y[now]));
	FOR(i,0,(int)G[now].size()-1){
		int v=G[now][i].fi;if(v==pre) continue;
		dfs(v,now,dep+G[now][i].se);
		H[now].join(H[v].rt);
		M[now].join(M[v].rt);
	}
	while(!H[now].empty()&&!M[now].empty()){
		node &a=H[now].top(),&b=M[now].top();
		ll nw=a.w+b.w-2*dep;
		int cnt=min(a.cnt,b.cnt);
		if(nw>=0) break;
		ans+=nw*cnt;
		a.cnt-=cnt,b.cnt-=cnt;if(!a.cnt) H[now].pop();if(!b.cnt) M[now].pop();
		H[now].push(node(-nw+a.w,cnt));
		M[now].push(node(-nw+b.w,cnt));
	}
} 
int main(){
	n=read();
	FOR(i,1,n-1){
		int u=read(),v=read(),c=read();
		G[u].push_back(make_pair(v,c));
		G[v].push_back(make_pair(u,c));
	}
	ll sum=0;
	FOR(i,1,n){
		x[i]=read(),y[i]=read();sum+=y[i]; 
	}
	dfs(1,0,0);
	printf("%lld\n",ans+sum*inf);
	return 0;
}

NOI2017 蔬菜

首先有個特別考思維的題意轉化費用流,然後貪心地用堆維護就行了,甚至不需要反悔。最後還有一個思維點,沒有退流,可以轉化成遞推。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章