2023牛客暑期多校赛第五场 B.Circle of Mistery

文章目录

  • 题目大意
  • 题解
      • 最优解及计算方法
      • 一个性质
      • 实现
  • 参考代码

题目大意

给出长度为 n ( 1 ≤ n ≤ 1 0 3 ) n(1\leq n \leq 10^3) n1n103 的数组 w w w k ( − 1 0 6 ≤ k ≤ 1 0 6 ) k(-10^6\leq k\leq 10^6) k(106k106)
要求构造一个排列 a a a ,连边 ( i , a i ) (i,a_i) (i,ai),显然的,会构成一个个环,要求至少一个环权值 w i w_i wi 之和大于 k k k
且该排列中逆序数对最少。

题解

最优解及计算方法

显然的,为了使逆序数对最少,
我们仅对一个符合条件的环进行操作。其他的位置 a i = i a_i=i ai=i
考虑逆序数对最小的情况,
我们构造 p a i = a i + 1 ( 1 ≤ i < s i z )   p s i z = a 1 p_{a_i}=a_{i+1}(1\leq ipai=ai+1(1i<siz) psiz=a1
a 1 a_1 a1左边的数或 a n a_n an 右边的数没有贡献,
环里的数除了最右边每个贡献为 1 1 1
a 1 ≤ i ≤ a n a_1 \leq i \leq a_n a1ian且不在环中的数贡献为 2 2 2 .
a n s = s i z − 1 + ( n − s i z − ( a 1 − 1 ) − ( n − a n ) ) ∗ 2 = 2 ∗ ( a n − a 1 ) − l + 1 ans=siz-1+(n-siz-(a_1-1)-(n-a_n))*2=2*(a_n-a_1)-l+1 ans=siz1+(nsiz(a11)(nan))2=2(ana1)l+1

一个性质

若左右皆固定,则个数越多越好。
我们发现在固定左端点时,环中个数固定的情况下,右端点加入该环,造成贡献为 − 1 -1 1
所以仅当加入右端点后至少还能使得一个元素加入,
才是一个优秀的解
左端点同理
所以我们可以看出左端点和右端点必大于

实现

枚举三个维度复杂度为 O ( N 3 ) O(N^3) O(N3),被卡掉了。
我们发现有大量重复的计算,考虑使用数据结构优化。
在环中的元素有两类。
①: w i > = 0 w_i>= 0 wi>=0 一定要加入
②: w i < 0 w_i<0 wi<0,用来凑数的。
想要②类足够多,优先加入权值大的。
确定使用的数据结构为带排序的优先队列。
我们将 w i < 0 w_i<0 wi<0 的元素加入优先队列 q 1 q1 q1,每次找到使得当前总值 > k >k >k 的数据量。
加入答案队列 q q q
由于之后可能会出现更大的负权值,所以我们要将预备列和答案队列维护交换一下使得
q 1 q1 q1 内的元素严格小于 q q q

参考代码

#include
using namespace std;
const int N=1e3+5,M=2e6+5;
const int inf=2e6;
int f[N];
priority_queue<int> q,q1;
int n,m,Ans=inf,tot;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f[i]);
        if(f[i]>=m)
            tot=1;
    }
    if(tot)
    {
        puts("0");
        return 0;
    }
    for(int l=1;l<=n;l++)
    {
        while(q.size())
            q.pop();
        while(q1.size())
            q1.pop();
        int ans=0,siz=0;
        if(f[l]>0)
            for(int r=l;r<=n;r++)
            {
                if(f[r]>0)
                {
                    siz++;
                    ans+=f[r];
                    int A=ans-m,c=siz;
                    if(A<0)
                        continue;
                    while(q1.size() && q.size() && q1.top()>-q.top())   //q中数据加负号使得栈顶为最小元素
               		{                                                     //维护答案队列
                        int a=q1.top();
                        int b=q.top();
                        q1.pop();
                        q.pop();
                        q.push(-a);
                        q1.push(-b);
                        A+=b;
                        A+=a;
                    }
                    while(q1.size() && A+q1.top()>=0)    //加入新答案
                    {
                        int a=q1.top();
                        q1.pop();
                        q.push(-a);
                        A+=a;
                        siz++;
                    }
                    Ans=min(2*(r-l)-siz+1,Ans);
                }
                else
                    q1.push(f[r]);
            }
    }
    if(Ans==inf)              //不存在权值和大于k
        puts("-1");
    else
       printf("%d\n",Ans);
}

你可能感兴趣的:(c++)