CF802O April Fools' Problem (hard)

luogu link\color{deeppink}link

CF link\color{deeppink}link

wqs\texttt{wqs} 做法

每天可以用 aia_i 的时间准备一道题和用 bib_i 的时间打印一道已经准备过的题,求打满 kk 道题的最小花费。

注意到随着 kk 增加,答案及斜率单调增,于是可以用 wqs\texttt{wqs} 贰分。

贰分每一个题需要的额外贡献,在每一个 aia_i 加上他,再用 bib_i 去匹配前面的某一个 aja_j,用反悔贪心即可实现。

具体的,维护一个小根堆,从 11nn 依次枚举一遍,先加入 aimida_i-mid,再用 bib_i 去匹配最小的 aa,然后在堆中加入 bi-b_i 表示反悔,但是要注意当 bib_i 匹配的值是正数的话是不可以进行匹配的,否则最后的 tottot 计数的并不是最优的,还可以减去这些正数的贡献。

复杂度是 O(nlognlogV)O(n\log n\log V)

线段树优化网络流做法

有一个显然的建图方式。

  • SSii 连一条 (1,ai)(1,a_i)
  • iiTT' 连一条 (1,bi)(1,b_i)
  • iii+1i+1 连一条 (inf,0)(\inf,0)
  • TT'TT 连一条 (k,0)(k,0)

我们将一个合法的匹配方式中的 aa 视为 ((bb 视为 )),这个匹配一定是合法的括号匹配。

考虑反悔机制。

插入一个 ()(),那么 [i,j)[i,j) 的前缀和数组 +1+1,插入一个 )()([i,j)[i,j) 减一。

可以维护以下东西:

  • mimi:区间前缀和最小值

  • val1val1()() 的最小值的 (i,j)(i,j)

  • val2val2)()( 的最小值的 (i,j)(i,j)

  • val3val3:满足区间前缀和最小值大于 mimi 的值最小的 (i,j)(i,j)

然后就是一大堆东西,Immaculacy 博客

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4e6;

int n,k,a[N],b[N];

struct node
{
	int val,f;
	
	friend bool operator<(node a,node b)
	{
		return a.val>b.val;
	}
};priority_queue<node>q;

main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];
	int l=0,r=2e9;
	while(l<=r)
	{
		while(!q.empty())q.pop();
		int mid=(l+r)>>1,res=0,num=0;
		for(int i=1;i<=n;i++)
		{
			int val=a[i]-mid;
			q.push((node){val,1});
			int p=b[i]+q.top().val;
			if(p<=0)
			{
				num+=q.top().f;res+=p;
				q.pop();q.push((node){-b[i],0});
			}
		}
		if(num==k)
		{
			cout<<res+k*mid;
			return 0;
		}
		if(num<k)l=mid+1;
		if(num>k) r=mid-1;
	}
}