A
居然直接莫队真的能过啊,当时考场完全想到了但是没敢写,后面实在不会做的时候应该冲一发的。
官方题解居然是个根号分治,所以 n n n\sqrt n nn 就是正解啊……
代码有空再补。
B
首先显然除了环上的点,其他点都是 p i = i p_i=i pi=i 的。
那么怎么找到一个逆序对数最少的环呢,肯定要研究一下环和逆序对数的关系,不可能枚举一个环然后去暴力算逆序对数的,肯定是根据环和逆序对数的关系,然后为了满足逆序对数最少而寻找构造环的方式。
还发现 n ≤ 1 0 3 n\leq 10^3 n≤103,也就是支持 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 len−1 个, 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,5→5,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) len−1+2∗(r−l+1−len) 即 r − l + ( r − l + 1 − l e n ) r-l+(r-l+1-len) r−l+(r−l+1−len), r − l r-l r−l 是区间长度-1, r − l + 1 − l e n r-l+1-len r−l+1−len 是区间内不在环上的点数,对于一个区间 r − l r-l r−l 是固定的,因此只能想办法让后者尽可能小,也就是让尽可能多的点在环上。
(额外补充一下, k ≤ 0 k\leq 0 k≤0 时的情况特判掉,现在只考虑 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();