CF1178H

发布时间 2023-12-23 20:45:14作者: yinhee

cdqz 两道题都很有意思啊!顺便是第一篇 *3500 题解。

先考虑第一问。

显然有单调性,所以可以二分。cdqz 这是二分专题吗

Lemma 1:所有操作都在 \(0\)\(t\) 时刻进行。

Proof:这是若干个一次函数,最大或最小值都会在端点处取得。所以是显然的。

接下来你就要使你在 \(t\) 时刻所拥有的股票价值尽可能大。所以在 \(0\) 时刻把所有股票换成能换的价值尽可能大的股票即可。看实现可以做到 \(O(n^2)\)\(O(n\log n)\) 完成 check。

接下来考虑第二问。发现每个 \([1,n]\) 的股票都对应一个 \([n+1,n\times 2]\) 中的。这使我们想到什么?二分图匹配。考虑网络流建模,跑 MCMF 解决。

首先每个股票有 \(0\) 时刻和 \(t\) 时刻两个状态,所以拆成两个点 \(a0_i,at_i\)

然后是连基本的 \((S,a0_i,1,0)(i\le n),(at_i,T,1,0)(i>n),(a0_i,at_i,inf,0)\)

最后就要刻画 \(0,t\) 时刻的换股票。一个 trival 的想法是枚举两个点 \(a_i,a_j\) 如果此时股票 \(i\) 的价值大于股票 \(j\) 的,那么可以换,即连 \((a_i,a_i,inf,1)\)

但是这样的时空是 \(O(n^2)\) 的,就算时间可以空间也不行。但是发现每次连的是排序后的一段前缀。所以可以前缀和优化建图。具体就是新增点 \(s0_i,st_i\),排序后连 \((s0_i,s0_{i-1},inf,0),(s0_i,a0_i,inf,0),(a0_i,s0_{i-1},inf,0)\)\(t\) 时刻同理。

然后跑 dinic 就做完了。

做起来感觉反而不难。

复杂度 \(O(n\log n\log v+\operatorname{maxflow})\)。后者远远顶不到时间复杂度上界。

我的常数怎么这么小?目前是最优解。就看 zlt 什么时候做完把我踢下去了。

有一点点小细节,就是排序的 cmp 要注意相等时的顺序,否则会 WA on test 113。

code:

点击查看代码
int n,m,nt,sum,dis[M],cur[M];
pair<ll,int> c[N];bool vis[M];
static int S,T;
int tot=1,head[M];
struct node{int to,nxt,fl,cw;}e[M<<1];
struct Node{int k,b,op;}a[N];
il void add(int u,int v,int f,int w){
	e[++tot]={v,head[u],f,w},head[u]=tot;
	e[++tot]={u,head[v],0,-w},head[v]=tot;
}
il ll calc(Node x,int y){return 1ll*x.k*y+x.b;}
il bool cmp1(Node x,Node y){return calc(x,0)==calc(y,0)?calc(x,nt)>calc(y,nt):calc(x,0)<calc(y,0);}
il bool cmp2(Node x,Node y){return calc(x,nt)>calc(y,nt);}
il bool cmp3(pair<ll,int> x,pair<ll,int> y){return x.fi==y.fi?a[x.se].op>a[y.se].op:x.fi<y.fi;}
bool check(int t){
	ll mx=-1ll*inf*inf;nt=t;
	priority_queue<ll> q;
	sort(a+1,a+n*2+1,cmp1);
	rep(i,1,n*2){
		mx=max(mx,calc(a[i],t));
		if(!a[i].op)q.push(mx);
	}
	sort(a+1,a+n*2+1,cmp2);
	rep(i,1,n*2){
		if(!a[i].op)continue;
		if(q.empty()||q.top()<calc(a[i],t))return 0;
		q.pop();
	}
	return 1;
}
int solve1(){
	int l=0,r=1e9,ans=1e9+1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid))r=(ans=mid)-1;
		else l=mid+1;
	}
	if(ans==1e9+1)puts("-1"),exit(0);
	return printf("%d ",ans),ans;
}
bool spfa(){
	mems(dis,0x3f),mems(vis,0);
	memcpy(cur,head,sizeof head);
	queue<int> q;
	dis[S]=0,vis[S]=1;
	q.push(S);
	while(q.size()){
		int u=q.front();q.pop();
		vis[u]=0;
		go(i,u){
			int v=e[i].to;
			if(!e[i].fl||dis[v]<=dis[u]+e[i].cw)continue;
			dis[v]=dis[u]+e[i].cw;
			if(!vis[v])vis[v]=1,q.push(v);
		}
	}
	return dis[T]<=1e9;
}
int dfs(int u,int f){
	if(u==T)return f;
	int s=0;vis[u]=1;
	for(int &i=cur[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(vis[v]||!e[i].fl||dis[v]!=dis[u]+e[i].cw)continue;
		int l=dfs(v,min(e[i].fl,f-s));
		e[i].fl-=l,e[i^1].fl+=l;
		s+=l,sum+=l*e[i].cw;
		if(s==f)break;
	}
	vis[u]=0;
	return s;
}
void dinic(){
	int ret=0;
	while(spfa())ret+=dfs(S,inf);
}
void solve2(int t){
	S=n*8+1,T=S+1;
	rep(i,1,n*2)if(!a[i].op)add(S,i,1,0);else add(n*2+i,T,1,0);
	rep(i,1,n*2)add(i,n*2+i,inf,0);
	rep(i,1,n*2)c[i]=Mp(calc(a[i],0),i);
	sort(c+1,c+n*2+1);
	add(n*4+c[1].se,c[1].se,inf,0);
	rep(i,2,n*2)add(c[i].se,n*4+c[i-1].se,inf,1),add(n*4+c[i].se,n*4+c[i-1].se,inf,0),add(n*4+c[i].se,c[i].se,inf,0);
	rep(i,1,n*2)c[i]=Mp(calc(a[i],t),i);
	sort(c+1,c+n*2+1,cmp3);
	add(n*6+c[1].se,n*2+c[1].se,inf,0);
	rep(i,2,n*2)add(n*2+c[i].se,n*6+c[i-1].se,inf,1),add(n*6+c[i].se,n*6+c[i-1].se,inf,0),add(n*6+c[i].se,n*2+c[i].se,inf,0);
	dinic(),printf("%d\n",sum);
}
void Yorushika(){
	scanf("%d",&n);
	rep(i,1,n)scanf("%d%d",&a[i].k,&a[i].b),a[i].op=0;
	rep(i,n+1,n*2)scanf("%d%d",&a[i].k,&a[i].b),a[i].op=1;
	int t=solve1();solve2(t);
}
signed main(){
	int t=1;
	//	scanf("%d",&t);
	while(t--)
		Yorushika();
}