2023牛客暑期多校第五场部分题解

博客围绕多个算法问题展开,涉及莫队、根号分治、逆序对环构造、竞赛图强联通分量判定、因子枚举、递归构造、双指针、动态规划、异或拆位等算法思路,还提及了部分代码实现思路,如用堆进行贪心、状态转移等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

A

居然直接莫队真的能过啊,当时考场完全想到了但是没敢写,后面实在不会做的时候应该冲一发的。

官方题解居然是个根号分治,所以 n n n\sqrt n nn 就是正解啊……

代码有空再补。

B

首先显然除了环上的点,其他点都是 p i = i p_i=i pi=i 的。

那么怎么找到一个逆序对数最少的环呢,肯定要研究一下环和逆序对数的关系,不可能枚举一个环然后去暴力算逆序对数的,肯定是根据环和逆序对数的关系,然后为了满足逆序对数最少而寻找构造环的方式。

还发现 n ≤ 1 0 3 n\leq 10^3 n103,也就是支持 O ( n 2 ) O(n^2) O(n2) 的算法,那么想到可以枚举每个区间,假如存在 O ( 1 ) O(1) O(1) O ( log ⁡ n ) O(\log n) O(logn) 构造出一个区间内的最优环就好了!

不妨假设区间 [ l , r ] [l,r] [l,r] 的左右端点都在环上,容易证明,只考虑环的话,环上的点产生的逆序对个数至少是 l e n − 1 len-1 len1 个, l e n len len 是环的长度,而要达到这个下界很容易的一种构造是 1 , 2 , 3 , 4 , 5 → 5 , 1 , 2 , 3 , 4 1,2,3,4,5\to5,1,2,3,4 1,2,3,4,55,1,2,3,4

再考虑区间内但是不在环上的点,假设将区间内的点排成一行,环上的点 i i i p i p_i pi 连一条边,那么不在环上的点一定被至少两条边跨过,要不然连的边肯定不成环。每一条跨过它的边意味着会有一个点跟他产生逆序对,所以每个不在环上的点至少产生 2 2 2 个逆序对。而发现上面那种简单的构造也能够让不在环上的点达到这个下界。

因此,对于一个区间 [ l , r ] [l,r] [l,r],环的长度为 l e n len len 时,产生的最少逆序对个数是 l e n − 1 + 2 ∗ ( r − l + 1 − l e n ) len-1+2*(r-l+1-len) len1+2(rl+1len) r − l + ( r − l + 1 − l e n ) r-l+(r-l+1-len) rl+(rl+1len) r − l r-l rl 是区间长度-1, r − l + 1 − l e n r-l+1-len rl+1len 是区间内不在环上的点数,对于一个区间 r − l r-l rl 是固定的,因此只能想办法让后者尽可能小,也就是让尽可能多的点在环上。

(额外补充一下, k ≤ 0 k\leq 0 k0 时的情况特判掉,现在只考虑 k > 0 k>0 k>0 的情况)

基于这个思路很容易想到一个区间的构造策略:正数都肯定要在环上,负数则从大往小尽可能取。但是不可能每次从头开始贪心,复杂度会爆炸,要从前一个区间的基础上贪。

负数的这个贪心用两个堆来整一下即可,代码如下:

#include <bits/stdc++.h>
using namespace std;
#define maxn 1010

int n,k,a[maxn];

int main()
{
	cin>>n>>k;
	int ma=-1e9;
	for(int i=1;i<=n;i++)
		cin>>a[i],ma=max(ma,a[i]);
	if(ma>=k){
		cout<<"0";
		return 0;
	}
	if(k<=0){
		cout<<"-1";
		return 0;
	}
	priority_queue<int> q1,q2;
	int ans=1e9;
	for(int i=1;i<=n;i++){
		int sum=0,out=0;
		while(!q1.empty())q1.pop();
		while(!q2.empty())q2.pop();
		for(int j=i;j<=n;j++){
			if(a[j]>=0)sum+=a[j];
			else q1.push(a[j]),out++;
			if(sum>=k){
				while(!q1.empty()&&!q2.empty()&&q1.top()+q2.top()>0){
					sum+=q1.top()+q2.top();
					q2.push(-q1.top());
					q1.push(-q2.top());
					q1.pop(),q2.pop();