CF1615H-Reindeer Games【保序回归,整体二分,网络流】

正题

题目链接:https://www.luogu.com.cn/problem/CF1615H


题目大意

有 n n n 个点,每个点有个初始权值 a i a_i ai ,你每次可以让一个点权值 + 1 +1 +1 或者 − 1 -1 −1 。

有 m m m 个限制要求某个点最终权值小于等于另一个点。

求最少的操作次数使得满足所有限制。

2 ≤ n , m ≤ 1000 , 1 ≤ a i ≤ 1 0 9 2\leq n,m\leq 1000,1\leq a_i\leq 10^9 2≤n,m≤1000,1≤ai≤109


解题思路

对于这类的保序回归问题,我们可以考虑整体二分,当前枚举到 m i d mid mid 时我们考虑将点权设为 m i d mid mid 或者 m i d + 1 mid+1 mid+1 时的最优情况,如果在这种情况下某个点被设置为 m i d mid mid 则最终值在 [ l , m i d ] [l,mid] [l,mid] 中,否则在 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 中。

然后我们考虑用网络流解决设置点权的问题,如果要求 b x ≤ b y b_x\leq b_y bx≤by ,那么当 b x b_x bx 被设置为 m i d + 1 mid+1 mid+1 时 b y b_y by 也要被设置为 m i d + 1 mid+1 mid+1 ,可以将题目变为一个最大权闭合图问题,使用网络流解决即可。


code

cpp 复制代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long
using namespace std;
const ll N=1100;
ll n,m,s,t,a[N],p[N],b[N];
ll pos[N],c[N],np[N],ans[N];
vector<ll> G[N];
namespace Net{
	struct node{
		ll to,next,w;
	}a[N<<2];
	ll dep[N],ls[N],tot,cnt;
	queue<ll> q;
	void addl(ll x,ll y,ll w){
		a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;
		a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;
	}
	bool bfs(){
		while(!q.empty())q.pop();
		for(ll i=1;i<=cnt;i++)dep[i]=0;
		q.push(s);dep[s]=1;
		while(!q.empty()){
			ll x=q.front();q.pop();
			for(ll i=ls[x];i;i=a[i].next){
				ll y=a[i].to;
				if(dep[y]||!a[i].w)continue;
				dep[y]=dep[x]+1;q.push(y);
				if(y==t)return true;
			}
		}
		return false;
	}
	ll dinic(ll x,ll flow){
		if(x==t)return flow;
		ll rest=0,k;
		for(ll i=ls[x];i;i=a[i].next){
			ll y=a[i].to;
			if(!a[i].w||dep[x]+1!=dep[y])continue;
			rest+=(k=dinic(y,min(a[i].w,flow-rest)));
			a[i].w-=k;a[i^1].w+=k;
			if(rest==flow)return rest;
		}
		if(!rest)dep[x]=0;
		return rest;
	}
	ll solve(){
		ll ans=0;
		while(bfs())
			ans+=dinic(s,1e18);
		bfs();
		return ans;
	}
	void init(){
		for(ll i=1;i<=cnt;i++)ls[i]=0;
		tot=1;s=1;t=2;
		return;
	}
}
void solve(ll l,ll r,ll L,ll R){
	if(l>r)return;
	if(L==R){
		for(ll i=l;i<=r;i++)ans[p[i]]=b[L];
		return;
	}
	ll mid=(L+R)>>1;
	Net::init();
	for(ll i=l;i<=r;i++){
		ll x=p[i];pos[x]=i;
		c[i]=abs(a[x]-b[mid])-abs(a[x]-b[mid+1]);
		if(c[i]>0)Net::addl(s,3+i-l,c[i]);
		else Net::addl(3+i-l,t,-c[i]);
	}
	for(ll i=l;i<=r;i++){
		ll x=p[i];
		for(ll z=0;z<G[x].size();z++)
			if(pos[G[x][z]]>=l&&pos[G[x][z]]<=r){
				ll j=pos[G[x][z]];
				Net::addl(3+i-l,3+j-l,1e18);
			}
	}
	for(ll i=l;i<=r;i++)pos[p[i]]=0;
	Net::cnt=3+r-l;
	Net::solve();
	ll xl=l,xr=r;
	for(ll i=l;i<=r;i++){
		if(Net::dep[3+i-l])np[xr--]=p[i];
		else np[xl++]=p[i];
	}
	for(ll i=l;i<=r;i++)p[i]=np[i];
	solve(l,xl-1,L,mid);
	solve(xr+1,r,mid+1,R);
	return;
}
signed main()
{
	ll k;
	scanf("%lld%lld",&n,&k);
	for(ll i=1;i<=n;i++)scanf("%lld",&a[i]),p[i]=i,b[i]=a[i];
	sort(b+1,b+1+n);
	m=unique(b+1,b+1+n)-b-1;
	for(ll i=1,x,y;i<=k;i++){
		scanf("%lld%lld",&x,&y);
		G[x].push_back(y);
	}
	solve(1,n,1,m);
	for(ll i=1;i<=n;i++)printf("%lld ",ans[i]);
	return 0;
}
相关推荐
多思考少编码21 天前
Codeforces Round 981 (Div. 3) A - E 详细题解(C++)
开发语言·c++·算法·codeforces·算法竞赛
大灰狼19132 个月前
【怎样基于Okhttp3来实现各种各样的远程调用,表单、JSON、文件、文件流等待】
java·springboot·网络流·okhttp3·文件流传输
wlwhonest2 个月前
newbie难度——暴力枚举
codeforces·暴力枚举
数学收藏家2 个月前
网络流之最小费用最大流(dinic+SPFA 模板)
网络流
Aurora_th2 个月前
Codeforces Round (Div.3) C.Sort (前缀和的应用)
c++·算法·前缀和·codeforces·观察力
多思考少编码3 个月前
Codeforces Round 969 (Div. 2) 题ABC详细题解,包含(C++,Python语言描述)
c++·python·算法·codeforces·算法竞赛
Aurora_th3 个月前
贪心算法的初涉(双指针 + “过山车思想”)
算法·leetcode·codeforces·贪心·双指针·“过山车”思想
摆烂小白敲代码4 个月前
Codeforces Round 963 (Div. 2)
c语言·数据结构·c++·算法·排序算法·codeforces
Code_Shark4 个月前
Codeforces Round 962 (Div. 3) A~F
c++·算法·codeforces·oi·acm-icpc