Rikka with Sequence II

本文提供了一种解决 Rikka with Sequence II 问题的有效算法。通过优化中位数的选择过程,结合折半查找技术,实现了 O(2^(n/2)*n) 的时间复杂度。此外,文章还详细介绍了如何使用特定的数据结构和排序策略来进一步提高算法效率。

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

题目链接

Rikka with Sequence II

题解

这题单纯枚举中位数在枚举两边子集是n^2的,可是我们发现其实排序之后,如果枚举到的位置是(i,j),即中位数mid=(a[i]+a[j])/2;则a[i+1…j-1]都不用考虑,直接把两边拿出来做折半查找就OK。

时间复杂度分析:考虑规模为i(0<=i<=n-2)的折半查找的做的次数为i+1,做一次的时间复杂度为 O(2i2) 那么总时间复杂度的计算如下

Ans==2n22(n1)+2n32(n2)+...2n22((n1)+n22+n34+...)

后面括号里面,大概可以看成 n+n2+n4+n8+... ,也就2n的样子?所以时间复杂度分析出来是O(2^(n/2)*n)的,我也觉得非常神奇,不考虑中间的数就能去掉一个n的复杂度

然后抽象成另外一个问题,把剩下的数全部拿出来,对于每个数,设两个权值a,b,如果数小于枚举出来的中位数,则a权值为-1,大于为1,b为原来的权值减去中位数,则要求就是选出一个子集使得

a=0,b<=0

怎么折半搜索?之前一直觉得需要一个map< int.set< int> >而且set还得兹磁rank操作,要不是这题带log不能过我估计我还真去写个treap(扶额),数据结构学傻了。实际上是这样的,先把所有枚举出来的子集按大小排序,然后维护2n个前缀和,表示在此之前有多少个是 a==i 的集合,然后two pointer 一下应该就可以不要log了,复杂度 O(n2n2) ,很棒啊。(所以如果再带一个排序或者二分查找的log就T了)

排序的时候需要用极其棒棒的类似归并的思想,因为全是正整数所以选了肯定不不选要的 b 要大,所以相对顺序不会变,然后归并一下就排好序了,复杂度只要 O(2n2)

代码稍后补上来。

然而我代码被卡常了,本机5.8s不能再少了,我也很迷啊,这个复杂度算出来。。我自带大常数吧,等吉司机的std拿出来了我再看我常数哪里写丑了。

为了卡常我还把wys的wc课间重新看了一遍,发现没卵用。

而且这代码很迷啊,改成 a 相等也能过样例,把返回的数组改成x也能过,好像自己出的n=40的数据也能过?好迷,静态差错才看出来。估计这题我是卡常卡不过去了,要不松爷您来帮我看下?(雾)vjudge上3days前的提交记录一片TLE,看了看代码发现和我其实是差不多的?(雾)

//QWsin
#include<set>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<assert.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const double eps=1e-9;
const int maxn=40+10;
const int maxm=1050000+10;

ll ans=0;
int a[maxn];
typedef pair<int,double> Node;
#define mp make_pair
#define a first
#define b second
//两种权值a=+-1,b=a[i]-中位数  

int top,SUM[maxn],vc_sz,tot;
Node t1[maxm],t2[maxm],t3[maxm],t4[maxm],vc[maxn];


inline void getsort(int l,int r,Node *t1,Node *t2,Node* &res,int &L)
{
    Node *x=t1,*y=t2;
    x[top=1]=mp(0,0);
    for(int i=l;i<=r;++i,swap(x,y))
    {
        int A=vc[i].a,B=vc[i].b;
        for(int j=1,k=top+1;j<=top;j+=4,k+=4) {
            x[k].a=x[j].a+A;
            x[k].b=x[j].b+B;
            x[k+1].a=x[j+1].a+A;
            x[k+1].b=x[j+1].b+B;
            x[k+2].a=x[j+2].a+A;
            x[k+2].b=x[j+2].b+B;
            x[k+3].a=x[j+3].a+A;
            x[k+3].b=x[j+3].b+B;
        }
        int p1=1,p2=top+1,p3=0;top<<=1;

//      if(x[top>>1] < x[(top>>1)+1]) {++tot;swap(x,y);continue;}

        while(p3<top)
            y[++p3]= p1>top>>1||(p2<=top&&x[p1].b > x[p2].b) ? x[p2++]:x[p1++];
    }
    L=top;
    res=x;
}

int L1,L2;

//对于vc里的Node计算sigma a=0 && sigma b <=0  的答案 
inline ll work()
{
    int sz=vc_sz;
    if(sz<2) return 1;
    Node *a1,*a2;

    getsort(1,sz>>1,t1,t2,a1,L1);
    getsort((sz>>1)+1,sz,t3,t4,a2,L2);

    memset(SUM,0,sizeof SUM);
    #define sum(i) SUM[i+20]

    ll ret=0;int p1=1;//反着来
    for(int p2=L2;p2>=1;--p2)
    {
        while(p1<=L1&&a1[p1].b+a2[p2].b <= eps)
            ++sum(a1[p1++].a);
        ret+=sum(-a2[p2].a);
    }
    return ret;
}
#undef a
#undef b
set<int>vis;
int main()
{
    int n;cin>>n; 
    for(int i=1;i<=n;++i) {
        scanf("%d",a+i);
        assert(!vis.count(a[i]));
        vis.insert(a[i]);
    }
    sort(a+1,a+n+1);


    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;++j)
        {
            double mid=(a[i]+a[j])/2;
            vc_sz=0;
            for(int k=1;k<i;++k)
                vc[++vc_sz]=mp(-1,a[k]-mid);
            for(int k=j+1;k<=n;++k)
                vc[++vc_sz]=mp(1,a[k]-mid);

            ans+=work();
        }
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值