CF1178H Stock Exchange
题目描述
简要题意:给定2n2n2n个一次函数y=aix+bi(a,b>0)y=a_ix+b_i(a,b>0)y=aix+bi(a,b>0),刚开始你有前nnn个函数各一个,在任意时刻ttt,xxx函数可以转换为yyy函数当且仅当axt+bx>=ayt+bya_xt+b_x>=a_yt+b_yaxt+bx>=ayt+by,最后要获得n+1...2nn+1...2nn+1...2n的函数各一个,求完成时刻最小的前提下转换次数的最小值。
Solution
显然最后的方案需要1...n1...n1...n和n+1...2nn+1...2nn+1...2n形成完美匹配。
有一个重要的性质:
一定存在一个最优方案使得每一个匹配的中转点唯一,且只在000时刻和ttt时刻进行交换。(显然对于某一匹配x−>yx->yx−>y的转化方案{x,z1,z2...zk,y}\{x,z_1,z_2...z_k,y\}{x,z1,z2...zk,y}来说,最多只需要取xxx,yyy,和一个{z1...zk}\{z_1...z_k\}{z1...zk}中斜率最大的zmaxaz_{maxa}zmaxa即可)
因此对于第一问,考虑二分答案完成时刻ttt,每一个1..n1..n1..n的函数在000时刻先贪心地转化为能转化的在ttt时刻最大的函数作为中转点,然后再考虑ttt时刻时能否通过这些最优中转点匹配出所有的n+1...2nn+1...2nn+1...2n的函数。
对于第二问,考虑费用流,把每一个点iii拆成iii和i+2ni+2ni+2n,从SSS向1..n1..n1..n连一条(flow=1,cost=0)(flow=1,cost=0)(flow=1,cost=0)的边,从n+1...2nn+1...2nn+1...2n向TTT连一条(1,0)(1,0)(1,0)的边,从iii向i+2ni+2ni+2n连一条(INF,0)(INF,0)(INF,0)的边,然后对于能在000时刻转化的i−>ji->ji−>j,连一条(INF,1)(INF,1)(INF,1)的边,在ttt时刻也做一次这样的操作。
但是这样的边数是O(n2)O(n^2)O(n2),超过了16M16M16M的空间限制。
我们发现对于iii来说,它可以转化的点是一段按bib_ibi排序的前缀,所以不需要每个i−>ji->ji−>j都连边了。
设排序之后的编号idiid_iidi,新建i+4ni+4ni+4n的点,从idi+4nid_i+4nidi+4n向idi−1+4nid_{i-1}+4nidi−1+4n连一条(INF,0)(INF,0)(INF,0)的边,再从idiid_iidi向idi+4nid_i+4nidi+4n连一条(INF,1)(INF,1)(INF,1)的边。
这样一来,i−>ji->ji−>j就可以通过一层一层的价格下降得到了。然后对于i+2ni+2ni+2n的点也新建i+6ni+6ni+6n按上述方法连边即可。
Code
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se secondusing namespace std;template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=998244353;
const int MAXN=4405;
const int MAXM=MAXN<<6;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{int f=1,x=0; char c=getchar();while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }return x*f;
}
queue<int> que;
ll c[MAXN],to[MAXN];
pair<ll,ll> a[MAXN];
int n,nn,vnum,edgenum=1,id0[MAXN],idt[MAXN],t,S,T;
int flow[MAXN<<2],dist[MAXN<<2],vis[MAXN<<2];
int head[MAXN<<2],pre[MAXN<<2],from[MAXN<<2];
struct enode{ int to,nxt,f,c; } e[MAXM];int compare0(int x,int y) { return (a[x].se<a[y].se)||(a[x].se==a[y].se&&a[x].fi>a[y].fi); }
int comparet(int x,int y) { return a[x].fi*t+a[x].se<a[y].fi*t+a[y].se; }
bool check(int x)
{t=x;for (int i=1;i<=nn;i++) c[i]=a[i].fi*t+a[i].se;for (int i=1,k=1;i<=nn;i++){if (c[id0[i]]>c[id0[k]]) k=i;to[id0[i]]=c[id0[k]];}sort(to+1,to+n+1);if (t==1e9)for (int i=1;i<=nn;i++) cout<<id0[i]<<endl;sort(idt+1,idt+nn+1,comparet);for (int i=1,now=1;i<=nn;i++) {if (idt[i]<=n) continue;if (to[now]<c[idt[i]]) return 0;else now++;}return 1;
}
int solve1()
{int l=0,r=1e9;while (l<r){int mid=(l+r)>>1;if (check(mid)) r=mid;else l=mid+1;}return r;
}void add(int u,int v,int f,int c)
{
// cout<<u<<" "<<v<<" "<<f<<" "<<c<<endl;e[++edgenum]=(enode){v,head[u],f, c},head[u]=edgenum;e[++edgenum]=(enode){u,head[v],0,-c},head[v]=edgenum;
}
void build() //这里把值相同的i+4n合并成了一个点,效果相同,空间压缩之后会更小一些,i+6n同理。
{S=nn<<1|1,T=vnum=S+1;for (int i=1;i<=n;i++) add(S,i,1,0);for (int i=n+1;i<=nn;i++) add(i+nn,T,1,0);for (int i=1;i<=nn;i++) add(i,i+nn,INF,0);for (int i=1;i<=nn;i++) c[i]=a[i].se;for (int i=1,j;i<=nn;i=j+1){j=i,vnum++;while (c[id0[i]]==c[id0[j+1]]) j++;for (int k=i;k<=j;k++) add(id0[k],vnum,INF,1),add(vnum,id0[k],INF,0);if (i>1) add(vnum,vnum-1,INF,0);}for (int i=1;i<=nn;i++) c[i]=a[i].fi*t+a[i].se;for (int i=1,j;i<=nn;i=j+1){j=i,vnum++;while (c[idt[i]]==c[idt[j+1]]) j++;for (int k=i;k<=j;k++) add(idt[k]+nn,vnum,INF,1),add(vnum,idt[k]+nn,INF,0);if (i>1) add(vnum,vnum-1,INF,0);}
}int bfs()
{for (int i=1;i<=vnum;i++) dist[i]=INF,flow[i]=vis[i]=0;dist[S]=0,flow[S]=INF,vis[S]=1,que.push(S);while (!que.empty()){int u=que.front(); que.pop();
// cout<<u<<":"<<dist[u]<<" "<<flow[u]<<endl;for (int i=head[u];i;i=e[i].nxt){int v=e[i].to,f=e[i].f,c=e[i].c;if (!f||dist[u]+e[i].c>=dist[v]) continue;pre[v]=u,from[v]=i;flow[v]=min(flow[u],f);dist[v]=dist[u]+e[i].c;if (!vis[v]) vis[v]=1,que.push(v);}vis[u]=0;}return flow[T];
}
int MCMF()
{int ans=0;while (bfs()){ans+=dist[T]*flow[T];for (int p=T;p!=S;p=pre[p])e[from[p]].f-=flow[T],e[from[p]^1].f+=flow[T];}return ans;
}
int main()
{n=read(),nn=n+n;for (int i=1;i<=nn;i++) a[i].fi=read(),a[i].se=read(),id0[i]=idt[i]=i;sort(id0+1,id0+nn+1,compare0);t=solve1();if (!check(t)) { puts("-1"); return 0; } printf("%d ",t);sort(idt+1,idt+nn,comparet);build();int ans=MCMF();printf("%d\n",ans);return 0;
}